/**
 * Integration tests for the bulk berth price-reconcile service (CM-2 Part A).
 *
 * Uses the real filesystem storage backend (seeded below) + a real spec-sheet
 * PDF, so the full upload → store → re-parse → extract path is exercised end to
 * end with no storage mock.
 */

import { readFileSync } from 'node:fs';
import path from 'node:path';

import { eq } from 'drizzle-orm';
import { beforeEach, describe, expect, it } from 'vitest';

import {
  listPriceReconciliation,
  applyBulkBerthPrices,
} from '@/lib/services/berth-price-reconcile.service';
import { uploadBerthPdf } from '@/lib/services/berth-pdf.service';
import { db } from '@/lib/db';
import { berths } from '@/lib/db/schema/berths';
import { systemSettings } from '@/lib/db/schema/system';

import { makeBerth, makeFullPermissions, makePort } from '../helpers/factories';
import { makeMockCtx, makeMockRequest } from '../helpers/route-tester';

const A1_PDF = readFileSync(path.join(process.cwd(), 'berth_pdf_example/Berth_Spec_Sheet_A1.pdf'));

beforeEach(async () => {
  await db
    .insert(systemSettings)
    .values({ key: 'storage_backend', value: 'filesystem', portId: null, updatedBy: null })
    .onConflictDoNothing();
});

describe('listPriceReconciliation', () => {
  it('parses the main price for a berth with a PDF and flags one without', async () => {
    const port = await makePort();
    const withPdf = await makeBerth({ portId: port.id, overrides: { mooringNumber: 'A1' } });
    // No-PDF berth — created for its 'no_pdf' row; the value isn't referenced.
    await makeBerth({ portId: port.id, overrides: { mooringNumber: 'Z9' } });

    await uploadBerthPdf({
      berthId: withPdf.id,
      portId: port.id,
      buffer: A1_PDF,
      fileName: 'Berth_Spec_Sheet_A1.pdf',
      uploadedBy: 'test-user',
    });

    const rows = await listPriceReconciliation(port.id);
    const w = rows.find((r) => r.mooringNumber === 'A1');
    const wo = rows.find((r) => r.mooringNumber === 'Z9');

    expect(w?.parsedPrice).toBe(3880800);
    expect(w?.parsedCurrency).toBe('USD');
    expect(w?.currentPrice).toBeNull();
    expect(w?.status).toBe('changed'); // CRM price null → changed
    expect(wo?.status).toBe('no_pdf');
  });
});

describe('applyBulkBerthPrices', () => {
  it('writes only approved, in-port berths and skips cross-port ids', async () => {
    const portA = await makePort();
    const portB = await makePort();
    const berthA = await makeBerth({ portId: portA.id, overrides: { mooringNumber: 'A1' } });
    const berthB = await makeBerth({ portId: portB.id, overrides: { mooringNumber: 'A1' } });

    const res = await applyBulkBerthPrices(
      portA.id,
      [
        { berthId: berthA.id, price: 3880800, currency: 'USD' },
        { berthId: berthB.id, price: 999, currency: 'USD' }, // foreign port → skipped
      ],
      'test-user',
    );

    expect(res.updated).toBe(1);

    const [a] = await db.select().from(berths).where(eq(berths.id, berthA.id));
    expect(Number(a!.price)).toBe(3880800);
    expect(a!.priceCurrency).toBe('USD');

    const [b] = await db.select().from(berths).where(eq(berths.id, berthB.id));
    expect(b!.price).toBeNull(); // untouched
  });
});

describe('price-reconcile route handlers', () => {
  it('GET lists rows and POST apply writes the approved price', async () => {
    const { getHandler } = await import('@/app/api/v1/berths/price-reconcile/handlers');
    const { postHandler } = await import('@/app/api/v1/berths/price-reconcile/apply/handlers');

    const port = await makePort();
    const berth = await makeBerth({ portId: port.id, overrides: { mooringNumber: 'A1' } });
    await uploadBerthPdf({
      berthId: berth.id,
      portId: port.id,
      buffer: A1_PDF,
      fileName: 'Berth_Spec_Sheet_A1.pdf',
      uploadedBy: 'test-user',
    });
    const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() });

    const listRes = await getHandler(
      makeMockRequest('GET', 'http://t/api/v1/berths/price-reconcile'),
      ctx,
      {},
    );
    const listJson = (await listRes.json()) as {
      data: Array<{ mooringNumber: string; parsedPrice: number | null }>;
    };
    expect(listJson.data.find((r) => r.mooringNumber === 'A1')?.parsedPrice).toBe(3880800);

    const applyRes = await postHandler(
      makeMockRequest('POST', 'http://t/api/v1/berths/price-reconcile/apply', {
        body: { approvals: [{ berthId: berth.id, price: 3880800, currency: 'USD' }] },
      }),
      ctx,
      {},
    );
    const applyJson = (await applyRes.json()) as { data: { updated: number } };
    expect(applyJson.data.updated).toBe(1);

    const [b] = await db.select().from(berths).where(eq(berths.id, berth.id));
    expect(Number(b!.price)).toBe(3880800);
  });
});
