/**
 * Default Sales ID Generator Port Adapter
 * Generates invoice numbers and IDs.
 */

import { randomUUID } from 'crypto';

import { pgQuery } from '@/lib/postgres/client';
import { SalesIdGeneratorPort } from '../application/sales-id-generator.port';

let invoiceNumberCounter = 0;

async function getNextPostgresInvoiceSequence(year: number): Promise<number> {
  const sequenceKey = `sales_invoice_seq_${year}`;

  // Seed the counter once from existing invoice numbers for this year, then increment atomically.
  await pgQuery(
    `
    INSERT INTO sequence_counters (id, raw, created_at, updated_at)
    SELECT
      $1::text,
      jsonb_build_object(
        'id',
        $1::text,
        'value',
        COALESCE(
          MAX(
            CASE
              WHEN split_part(invoice_number, '/', 1) = 'S'
                AND split_part(invoice_number, '/', 2) = $2::text
                AND split_part(invoice_number, '/', 3) ~ '^[0-9]+$'
              THEN split_part(invoice_number, '/', 3)::bigint
              ELSE NULL
            END
          ),
          0
        )
      ),
      NOW(),
      NOW()
    FROM invoices
    ON CONFLICT (id) DO NOTHING
    `,
    [sequenceKey, String(year)]
  );

  const result = await pgQuery<{ next_value: string }>(
    `
    UPDATE sequence_counters
    SET
      raw = jsonb_set(
        raw,
        '{value}',
        to_jsonb(COALESCE((raw->>'value')::bigint, 0) + 1),
        true
      ),
      updated_at = NOW()
    WHERE id = $1::text
    RETURNING (raw->>'value')::text AS next_value
    `,
    [sequenceKey]
  );

  const value = Number(result.rows[0]?.next_value || 0);
  if (!Number.isFinite(value) || value <= 0) {
    throw new Error('Failed to generate next sales invoice sequence value.');
  }

  return value;
}

async function getNextPostgresStoreInvoiceSequence(year: number): Promise<number> {
  const sequenceKey = `store_invoice_seq_${year}`;

  await pgQuery(
    `
    INSERT INTO sequence_counters (id, raw, created_at, updated_at)
    SELECT
      $1::text,
      jsonb_build_object(
        'id',
        $1::text,
        'value',
        COALESCE(
          MAX(
            CASE
              WHEN split_part(invoice_number, '/', 1) = 'ST'
                AND split_part(invoice_number, '/', 2) = $2::text
                AND split_part(invoice_number, '/', 3) ~ '^[0-9]+$'
              THEN split_part(invoice_number, '/', 3)::bigint
              ELSE NULL
            END
          ),
          0
        )
      ),
      NOW(),
      NOW()
    FROM invoices
    ON CONFLICT (id) DO NOTHING
    `,
    [sequenceKey, String(year)]
  );

  const result = await pgQuery<{ next_value: string }>(
    `
    UPDATE sequence_counters
    SET
      raw = jsonb_set(
        raw,
        '{value}',
        to_jsonb(COALESCE((raw->>'value')::bigint, 0) + 1),
        true
      ),
      updated_at = NOW()
    WHERE id = $1::text
    RETURNING (raw->>'value')::text AS next_value
    `,
    [sequenceKey]
  );

  const value = Number(result.rows[0]?.next_value || 0);
  if (!Number.isFinite(value) || value <= 0) {
    throw new Error('Failed to generate next store invoice sequence value.');
  }

  return value;
}

async function getNextPostgresShipmentSequence(year: number): Promise<number> {
  const sequenceKey = `sales_shipment_seq_${year}`;

  await pgQuery(
    `
    INSERT INTO sequence_counters (id, raw, created_at, updated_at)
    SELECT
      $1::text,
      jsonb_build_object(
        'id',
        $1::text,
        'value',
        COALESCE(
          MAX(
            CASE
              WHEN split_part(invoice_number, '/', 1) = 'SHP'
                AND split_part(invoice_number, '/', 2) = $2::text
                AND split_part(invoice_number, '/', 3) ~ '^[0-9]+$'
              THEN split_part(invoice_number, '/', 3)::bigint
              ELSE NULL
            END
          ),
          0
        )
      ),
      NOW(),
      NOW()
    FROM invoices
    ON CONFLICT (id) DO NOTHING
    `,
    [sequenceKey, String(year)]
  );

  const result = await pgQuery<{ next_value: string }>(
    `
    UPDATE sequence_counters
    SET
      raw = jsonb_set(
        raw,
        '{value}',
        to_jsonb(COALESCE((raw->>'value')::bigint, 0) + 1),
        true
      ),
      updated_at = NOW()
    WHERE id = $1::text
    RETURNING (raw->>'value')::text AS next_value
    `,
    [sequenceKey]
  );

  const value = Number(result.rows[0]?.next_value || 0);
  if (!Number.isFinite(value) || value <= 0) {
    throw new Error('Failed to generate next sales shipment sequence value.');
  }

  return value;
}

export class DefaultSalesIdGeneratorPort implements SalesIdGeneratorPort {
  /**
   * Generate next invoice number in format: S/YYYY/XXXX
   */
  async generateInvoiceNumber(): Promise<string> {
    try {
      const year = new Date().getFullYear();
      const sequence = await getNextPostgresInvoiceSequence(year);      const number = String(sequence).padStart(4, '0');

      return `S/${year}/${number}`;
    } catch (error) {
      console.error('Failed to generate invoice number:', error);
      throw error;
    }
  }

  /**
   * Generate next store invoice number in format: ST/YYYY/XXXX
   */
  async generateStoreInvoiceNumber(): Promise<string> {
    try {
      const year = new Date().getFullYear();
      const sequence = await getNextPostgresStoreInvoiceSequence(year);
      const number = String(sequence).padStart(4, '0');
      return `ST/${year}/${number}`;
    } catch (error) {
      console.error('Failed to generate store invoice number:', error);
      throw error;
    }
  }

  /**
   * Generate next shipment number in format: SHP/YYYY/XXXX
   */
  async generateShipmentNumber(): Promise<string> {
    try {
      const year = new Date().getFullYear();
      const sequence = await getNextPostgresShipmentSequence(year);
      const number = String(sequence).padStart(4, '0');
      return `SHP/${year}/${number}`;
    } catch (error) {
      console.error('Failed to generate shipment number:', error);
      throw error;
    }
  }

  /**
   * Generate UUID for invoice ID
   */
  async generateInvoiceId(): Promise<string> {
    return `inv-${randomUUID()}`;
  }

  /**
   * Generate UUID for activity log entry
   */
  async generateActivityLogId(): Promise<string> {
    return `log-${randomUUID()}`;
  }

  /**
   * Generate UUID for document attachment
   */
  async generateDocumentId(): Promise<string> {
    return `doc-${randomUUID()}`;
  }
}
