import { randomUUID } from "node:crypto";
import { getPool, withClient } from "../db/pool";

export interface OutletEntity {
  id: string;
  merchant_id: string;
  outlet_code: string;
  name: string;
  address?: string;
  status: "active" | "inactive";
  created_at: string;
  updated_at: string;
}

export interface TerminalEntity {
  id: string;
  outlet_id: string;
  terminal_code: string;
  qr_mode: "static" | "dynamic_mqtt" | "dynamic_api";
  partner_reference?: string;
  status: "active" | "inactive";
  created_at: string;
  updated_at: string;
}

function nowIso() {
  return new Date().toISOString();
}

function makeCode(prefix: string, id: string) {
  return `${prefix}_${id.slice(0, 6)}`;
}

function mapOutlet(row: any): OutletEntity {
  return {
    id: row.id,
    merchant_id: row.merchant_id,
    outlet_code: row.outlet_code,
    name: row.name,
    address: row.address || undefined,
    status: row.status,
    created_at: row.created_at,
    updated_at: row.updated_at
  };
}

function mapTerminal(row: any): TerminalEntity {
  return {
    id: row.id,
    outlet_id: row.outlet_id,
    terminal_code: row.terminal_code,
    qr_mode: row.qr_mode,
    partner_reference: row.partner_reference || undefined,
    status: row.status,
    created_at: row.created_at,
    updated_at: row.updated_at
  };
}

export async function createOutlet(payload: {
  merchant_id: string;
  name: string;
  address?: string;
  outlet_code?: string;
  status?: OutletEntity["status"];
}): Promise<OutletEntity> {
  const id = randomUUID();
  const now = nowIso();

  const { rows } = await getPool().query(
    `INSERT INTO outlets (id, merchant_id, outlet_code, name, address, status, created_at, updated_at)
     VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
     RETURNING *`,
    [
      id,
      payload.merchant_id,
      payload.outlet_code || makeCode("out", id),
      payload.name,
      payload.address,
      payload.status || "active",
      now,
      now
    ]
  );

  return mapOutlet(rows[0]);
}

export async function createTerminal(payload: {
  outlet_id: string;
  terminal_code?: string;
  qr_mode?: TerminalEntity["qr_mode"];
  partner_reference?: string;
  status?: TerminalEntity["status"];
}): Promise<TerminalEntity> {
  const id = randomUUID();
  const now = nowIso();

  const { rows } = await getPool().query(
    `INSERT INTO terminals (id, outlet_id, terminal_code, qr_mode, partner_reference, status, created_at, updated_at)
     VALUES ($1,$2,$3,$4,$5,$6,$7,$8)
     RETURNING *`,
    [
      id,
      payload.outlet_id,
      payload.terminal_code || makeCode("term", id),
      payload.qr_mode || "static",
      payload.partner_reference || null,
      payload.status || "active",
      now,
      now
    ]
  );

  return mapTerminal(rows[0]);
}

export async function listOutlets(filter?: {
  merchant_id?: string;
  status?: OutletEntity["status"];
  q?: string;
}): Promise<OutletEntity[]> {
  const clauses: string[] = [];
  const params: unknown[] = [];
  let i = 1;

  if (filter?.merchant_id) {
    clauses.push(`merchant_id = $${i++}`);
    params.push(filter.merchant_id);
  }

  if (filter?.status) {
    clauses.push(`status = $${i++}`);
    params.push(filter.status);
  }

  if (filter?.q) {
    const value = `%${filter.q.toLowerCase()}%`;
    clauses.push(`(LOWER(name) LIKE $${i++} OR LOWER(outlet_code) LIKE $${i++})`);
    params.push(value, value);
  }

  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
  const { rows } = await getPool().query(
    `SELECT * FROM outlets ${where} ORDER BY created_at DESC`,
    params
  );
  return rows.map(mapOutlet);
}

export async function listTerminals(filter?: {
  outlet_id?: string;
  status?: TerminalEntity["status"];
  q?: string;
}): Promise<TerminalEntity[]> {
  const clauses: string[] = [];
  const params: unknown[] = [];
  let i = 1;

  if (filter?.outlet_id) {
    clauses.push(`outlet_id = $${i++}`);
    params.push(filter.outlet_id);
  }

  if (filter?.status) {
    clauses.push(`status = $${i++}`);
    params.push(filter.status);
  }

  if (filter?.q) {
    const value = `%${filter.q.toLowerCase()}%`;
    clauses.push(`(LOWER(terminal_code) LIKE $${i++} OR LOWER(COALESCE(partner_reference, '')) LIKE $${i++})`);
    params.push(value, value);
  }

  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
  const { rows } = await getPool().query(
    `SELECT * FROM terminals ${where} ORDER BY created_at DESC`,
    params
  );
  return rows.map(mapTerminal);
}

export async function getOutletById(id: string): Promise<OutletEntity | null> {
  const { rows } = await getPool().query("SELECT * FROM outlets WHERE id = $1", [id]);
  return rows[0] ? mapOutlet(rows[0]) : null;
}

export async function getTerminalById(id: string): Promise<TerminalEntity | null> {
  const { rows } = await getPool().query("SELECT * FROM terminals WHERE id = $1", [id]);
  return rows[0] ? mapTerminal(rows[0]) : null;
}

export async function patchOutlet(id: string, patch: Partial<OutletEntity>): Promise<OutletEntity> {
  const existing = await getOutletById(id);
  if (!existing) {
    throw new Error("OUTLET_NOT_FOUND");
  }

  const merged = { ...existing, ...patch, updated_at: nowIso() };

  const { rows } = await getPool().query(
    `UPDATE outlets
        SET merchant_id = $2,
            outlet_code = $3,
            name = $4,
            address = $5,
            status = $6,
            updated_at = $7
      WHERE id = $1
      RETURNING *`,
    [
      id,
      merged.merchant_id,
      merged.outlet_code,
      merged.name,
      merged.address || null,
      merged.status,
      merged.updated_at
    ]
  );

  return mapOutlet(rows[0]);
}

export async function patchTerminal(id: string, patch: Partial<TerminalEntity>): Promise<TerminalEntity> {
  const existing = await getTerminalById(id);
  if (!existing) {
    throw new Error("TERMINAL_NOT_FOUND");
  }

  const merged = { ...existing, ...patch, updated_at: nowIso() };

  const { rows } = await getPool().query(
    `UPDATE terminals
        SET outlet_id = $2,
            terminal_code = $3,
            qr_mode = $4,
            partner_reference = $5,
            status = $6,
            updated_at = $7
      WHERE id = $1
      RETURNING *`,
    [
      id,
      merged.outlet_id,
      merged.terminal_code,
      merged.qr_mode,
      merged.partner_reference || null,
      merged.status,
      merged.updated_at
    ]
  );

  return mapTerminal(rows[0]);
}

export function toOutletPayload(outlet: OutletEntity) {
  return { ...outlet };
}

export function toTerminalPayload(terminal: TerminalEntity) {
  return { ...terminal };
}
