import type pg from 'pg';
import type { PosTerminalRepository } from '../domain/ports.js';
import type { PosTerminal, ListTerminalsOptions } from '../domain/terminal.js';

interface TerminalRow {
  id: string;
  store_id: string;
  name: string;
  binding_code: string | null;
  bound_at: Date | null;
  status: 'active' | 'disabled' | 'decommissioned';
  interface_mode: 'desktop' | 'touch' | 'auto';
  settings: Record<string, unknown>;
  last_seen_at: Date | null;
  created_at: Date;
  updated_at: Date;
}

function toTerminal(row: TerminalRow): PosTerminal {
  return {
    id: row.id,
    storeId: row.store_id,
    name: row.name,
    bindingCode: row.binding_code,
    boundAt: row.bound_at,
    status: row.status,
    interfaceMode: row.interface_mode,
    settings: row.settings,
    lastSeenAt: row.last_seen_at,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export class PgTerminalRepository implements PosTerminalRepository {
  constructor(private readonly pool: pg.Pool) {}

  async findById(id: string): Promise<PosTerminal | undefined> {
    const result = await this.pool.query<TerminalRow>(
      'SELECT * FROM pos_terminals WHERE id = $1',
      [id],
    );
    return result.rows[0] ? toTerminal(result.rows[0]) : undefined;
  }

  async findByBindingCode(code: string): Promise<PosTerminal | undefined> {
    const result = await this.pool.query<TerminalRow>(
      'SELECT * FROM pos_terminals WHERE binding_code = $1',
      [code],
    );
    return result.rows[0] ? toTerminal(result.rows[0]) : undefined;
  }

  async list(options: ListTerminalsOptions = {}): Promise<{ terminals: PosTerminal[]; total: number }> {
    const { storeId, status, limit = 50, offset = 0 } = options;
    const conditions: string[] = [];
    const params: unknown[] = [];
    if (storeId) { params.push(storeId); conditions.push(`store_id = $${params.length}`); }
    if (status) { params.push(status); conditions.push(`status = $${params.length}`); }
    const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
    const countParams = params.length;

    params.push(limit, offset);
    const [countResult, listResult] = await Promise.all([
      this.pool.query<{ count: string }>(`SELECT COUNT(*) as count FROM pos_terminals ${where}`, params.slice(0, countParams)),
      this.pool.query<TerminalRow>(
        `SELECT * FROM pos_terminals ${where} ORDER BY name LIMIT $${params.length - 1} OFFSET $${params.length}`,
        params,
      ),
    ]);

    return {
      terminals: listResult.rows.map(toTerminal),
      total: parseInt(countResult.rows[0]?.count ?? '0', 10),
    };
  }

  async updateLastSeen(id: string): Promise<void> {
    await this.pool.query(
      'UPDATE pos_terminals SET last_seen_at = now(), updated_at = now() WHERE id = $1',
      [id],
    );
  }

  async bind(id: string, bindingCode: string): Promise<PosTerminal> {
    const result = await this.pool.query<TerminalRow>(
      `UPDATE pos_terminals
       SET binding_code = $2, bound_at = now(), status = 'active', updated_at = now()
       WHERE id = $1 RETURNING *`,
      [id, bindingCode],
    );
    if (!result.rows[0]) throw new Error(`Terminal ${id} not found`);
    return toTerminal(result.rows[0]);
  }
}
