import type { PoolClient } from 'pg';
import { withTransaction } from '../db.js';
import { recordActivity, recordSyncEvent } from './audit.js';
import type { AuthUser, ServicePartInput } from '../types.js';

export async function assertTicketAccess(client: PoolClient, ticketId: string, user: AuthUser) {
  const { rows } = await client.query(
    `SELECT st.*, e.user_id AS engineer_user_id
     FROM service_tickets st
     LEFT JOIN engineers e ON e.id=st.assigned_engineer_id
     WHERE st.id=$1 FOR UPDATE`,
    [ticketId]
  );
  const ticket = rows[0];
  if (!ticket) throw new Error('Ticket not found.');
  if (user.role === 'engineer' && ticket.engineer_user_id !== user.id) throw new Error('This job is not assigned to you.');
  return ticket;
}

export async function startTicket(input: {
  ticketId: string;
  actor: AuthUser;
  latitude?: number;
  longitude?: number;
  accuracy?: number;
  clientActionId?: string;
}) {
  return withTransaction(async (client) => {
    const ticket = await assertTicketAccess(client, input.ticketId, input.actor);
    if (['completed', 'cancelled'].includes(ticket.status)) throw new Error(`Ticket cannot be started from ${ticket.status}.`);

    const { rows } = await client.query(
      `UPDATE service_tickets
       SET status='in_progress', started_at=COALESCE(started_at,NOW()), location_lat=COALESCE($2,location_lat), location_lng=COALESCE($3,location_lng), updated_by=$4
       WHERE id=$1
       RETURNING *`,
      [input.ticketId, input.latitude ?? null, input.longitude ?? null, input.actor.id]
    );
    if (input.latitude !== undefined && input.longitude !== undefined && ticket.assigned_engineer_id) {
      await client.query(
        `INSERT INTO engineer_location_logs (engineer_id,ticket_id,latitude,longitude,accuracy_m,created_by)
         VALUES ($1,$2,$3,$4,$5,$6)`,
        [ticket.assigned_engineer_id, input.ticketId, input.latitude, input.longitude, input.accuracy ?? null, input.actor.id]
      );
    }
    await client.query(
      `INSERT INTO service_ticket_logs (ticket_id,event_type,message,payload,actor_user_id)
       VALUES ($1,'ticket.started','Engineer started the service job',$2::jsonb,$3)`,
      [input.ticketId, JSON.stringify({ latitude: input.latitude, longitude: input.longitude }), input.actor.id]
    );
    await recordActivity(client, { actorUserId: input.actor.id, eventType: 'ticket.started', entityType: 'service_ticket', entityId: input.ticketId, message: 'Service job started.' });
    await recordSyncEvent(client, { clientActionId: input.clientActionId, eventType: 'ticket.started', entityType: 'service_ticket', entityId: input.ticketId, actorUserId: input.actor.id, payload: { status: 'in_progress' } });
    return rows[0];
  });
}

export async function appendTicketNote(input: {
  ticketId: string;
  actor: AuthUser;
  note: string;
  clientActionId?: string;
}) {
  return withTransaction(async (client) => {
    await assertTicketAccess(client, input.ticketId, input.actor);
    await client.query(
      `INSERT INTO service_ticket_logs (ticket_id,event_type,message,actor_user_id)
       VALUES ($1,'ticket.note',$2,$3)`,
      [input.ticketId, input.note, input.actor.id]
    );
    await recordActivity(client, { actorUserId: input.actor.id, eventType: 'ticket.note', entityType: 'service_ticket', entityId: input.ticketId, message: input.note });
    await recordSyncEvent(client, { clientActionId: input.clientActionId, eventType: 'ticket.note', entityType: 'service_ticket', entityId: input.ticketId, actorUserId: input.actor.id, payload: { note: input.note } });
    return { ticketId: input.ticketId, note: input.note };
  });
}

export async function completeTicket(input: {
  ticketId: string;
  actor: AuthUser;
  diagnosis: string;
  workDone: string;
  pendingIssue?: string;
  revisitRequired?: boolean;
  serviceCharge?: number;
  collectedAmount?: number;
  paymentMethod?: string;
  customerSignature?: string;
  customerSignerName?: string;
  customerRating?: number;
  customerFeedback?: string;
  parts?: ServicePartInput[];
  clientActionId?: string;
}) {
  return withTransaction(async (client) => {
    const ticket = await assertTicketAccess(client, input.ticketId, input.actor);
    if (ticket.status === 'completed') return ticket;
    if (ticket.status === 'cancelled') throw new Error('Cancelled ticket cannot be completed.');

    for (const part of input.parts ?? []) {
      const { rows: productRows } = await client.query(
        'SELECT id,name,current_stock,purchase_cost FROM products WHERE id=$1 AND is_active=true FOR UPDATE',
        [part.productId]
      );
      const product = productRows[0];
      if (!product) throw new Error('One of the selected parts no longer exists.');
      if (Number(product.current_stock) < part.quantity) throw new Error(`Insufficient stock for ${product.name}.`);
      const balanceAfter = Number(product.current_stock) - part.quantity;
      await client.query('UPDATE products SET current_stock=$2 WHERE id=$1', [part.productId, balanceAfter]);
      await client.query(
        `INSERT INTO ticket_parts_used (ticket_id,product_id,quantity,billing_type,unit_price,note,created_by)
         VALUES ($1,$2,$3,$4,$5,$6,$7)`,
        [input.ticketId, part.productId, part.quantity, part.billingType ?? 'warranty', part.unitPrice ?? Number(product.purchase_cost), part.note ?? null, input.actor.id]
      );
      await client.query(
        `INSERT INTO stock_movements (product_id,movement_type,reference_type,reference_id,quantity_out,balance_after,unit_cost,total_value,note,created_by)
         VALUES ($1,'service_issue','service_ticket',$2,$3,$4,$5,$6,$7,$8)`,
        [part.productId, input.ticketId, part.quantity, balanceAfter, Number(product.purchase_cost), Number(product.purchase_cost) * part.quantity, part.note ?? 'Issued for service ticket', input.actor.id]
      );
    }

    const nextStatus = input.revisitRequired ? 'revisit_required' : 'completed';
    const { rows } = await client.query(
      `UPDATE service_tickets
       SET status=$2, completed_at=NOW(), diagnosis=$3, work_done=$4, pending_issue=$5, revisit_required=$6,
           service_charge=$7, collected_amount=$8, payment_method=$9, customer_signature=$10,
           customer_signer_name=$11, customer_rating=$12, customer_feedback=$13, updated_by=$14
       WHERE id=$1
       RETURNING *`,
      [input.ticketId, nextStatus, input.diagnosis, input.workDone, input.pendingIssue ?? null, Boolean(input.revisitRequired), input.serviceCharge ?? 0, input.collectedAmount ?? 0, input.paymentMethod ?? null, input.customerSignature ?? null, input.customerSignerName ?? null, input.customerRating ?? null, input.customerFeedback ?? null, input.actor.id]
    );

    if (Number(input.collectedAmount ?? 0) > 0) {
      await client.query(
        `INSERT INTO journal_entries (entry_date,entry_type,party_type,customer_id,debit,credit,reference,description,created_by)
         VALUES (CURRENT_DATE,'service_collection','customer',$1,$2,0,$3,$4,$5)`,
        [ticket.customer_id, input.collectedAmount, `TICKET:${ticket.ticket_number}`, `Service collection via ${input.paymentMethod ?? 'unspecified'}`, input.actor.id]
      );
    }

    await client.query(
      `INSERT INTO service_ticket_logs (ticket_id,event_type,message,payload,actor_user_id)
       VALUES ($1,'ticket.completed','Engineer completed the service job',$2::jsonb,$3)`,
      [input.ticketId, JSON.stringify({ status: nextStatus, partsCount: (input.parts ?? []).length, collectedAmount: input.collectedAmount ?? 0 }), input.actor.id]
    );
    await recordActivity(client, { actorUserId: input.actor.id, eventType: 'ticket.completed', entityType: 'service_ticket', entityId: input.ticketId, message: `Service job ${nextStatus}.`, metadata: { partsCount: (input.parts ?? []).length } });
    await recordSyncEvent(client, { clientActionId: input.clientActionId, eventType: 'ticket.completed', entityType: 'service_ticket', entityId: input.ticketId, actorUserId: input.actor.id, payload: { status: nextStatus, collectedAmount: input.collectedAmount ?? 0 } });
    return rows[0];
  });
}
