/**
 * Integration test: commit runner + undo.
 *
 * Commits a 3-row companies "file" (skip existing / insert new / error blank)
 * and asserts counts, per-row ledger, and the actual inserted entity. Then
 * undoes the batch and asserts the inserted company is gone. Real test DB.
 */
import { beforeAll, describe, expect, it } from 'vitest';
import { and, eq, sql } from 'drizzle-orm';

import { db } from '@/lib/db';
import { importBatches, importBatchRows } from '@/lib/db/schema/imports';
import { companies } from '@/lib/db/schema/companies';
import { commitBatch, undoBatch } from '@/lib/import/commit';
import { companiesAdapter } from '@/lib/import/adapters/companies';
import type { ConflictPolicy, RawRow } from '@/lib/import/types';

let makePort: typeof import('../helpers/factories').makePort;
let makeCompany: typeof import('../helpers/factories').makeCompany;
let makeAuditMeta: typeof import('../helpers/factories').makeAuditMeta;

beforeAll(async () => {
  const f = await import('../helpers/factories');
  makePort = f.makePort;
  makeCompany = f.makeCompany;
  makeAuditMeta = f.makeAuditMeta;
});

async function makeBatch(portId: string, policy: ConflictPolicy): Promise<string> {
  const [b] = await db
    .insert(importBatches)
    .values({
      portId,
      entityType: 'companies',
      filename: 'companies.csv',
      storageKey: 'unused-in-direct-commit',
      mappingJson: { name: 'Name' },
      conflictPolicy: policy,
      createdBy: 'tester',
    })
    .returning({ id: importBatches.id });
  return b!.id;
}

describe('commitBatch + undoBatch', () => {
  const rows: RawRow[] = [{ Name: 'Acme Marine' }, { Name: 'Fresh Imports Ltd' }, { Name: '' }];

  it('commits (skip/insert/error), records the ledger, then undoes the insert', async () => {
    const port = await makePort();
    await makeCompany({ portId: port.id, overrides: { name: 'Acme Marine' } });
    const batchId = await makeBatch(port.id, 'skip-matches');
    const ctx = { portId: port.id, meta: makeAuditMeta({ portId: port.id }) };

    const counts = await commitBatch({
      batchId,
      adapter: companiesAdapter,
      rawRows: rows,
      mapping: { name: 'Name' },
      policy: 'skip-matches',
      ctx,
    });
    expect(counts).toEqual({ inserted: 1, updated: 0, skipped: 1, errored: 1 });

    const [batch] = await db.select().from(importBatches).where(eq(importBatches.id, batchId));
    expect(batch!.status).toBe('completed');
    expect(batch!.inserted).toBe(1);

    const ledger = await db
      .select()
      .from(importBatchRows)
      .where(eq(importBatchRows.batchId, batchId))
      .orderBy(importBatchRows.rowNumber);
    expect(ledger.map((r) => r.action)).toEqual(['skipped', 'inserted', 'errored']);
    expect(ledger[2]!.error).toBeTruthy();

    // The new company really exists.
    const fresh = await db
      .select({ id: companies.id })
      .from(companies)
      .where(
        and(eq(companies.portId, port.id), sql`lower(${companies.name}) = 'fresh imports ltd'`),
      );
    expect(fresh).toHaveLength(1);

    // Undo removes exactly the inserted row.
    const undo = await undoBatch(batchId, port.id);
    expect(undo.deleted).toBe(1);
    expect(undo.blocked).toBe(0);

    const afterUndo = await db
      .select({ id: companies.id })
      .from(companies)
      .where(
        and(eq(companies.portId, port.id), sql`lower(${companies.name}) = 'fresh imports ltd'`),
      );
    expect(afterUndo).toHaveLength(0);

    const [undoneBatch] = await db
      .select({ status: importBatches.status })
      .from(importBatches)
      .where(eq(importBatches.id, batchId));
    expect(undoneBatch!.status).toBe('undone');
  });

  it('update-matches overwrites the matched company', async () => {
    const port = await makePort();
    await makeCompany({
      portId: port.id,
      overrides: { name: 'Acme Marine', legalName: 'OLD' },
    });
    const batchId = await makeBatch(port.id, 'update-matches');
    const ctx = { portId: port.id, meta: makeAuditMeta({ portId: port.id }) };

    const counts = await commitBatch({
      batchId,
      adapter: companiesAdapter,
      rawRows: [{ Name: 'Acme Marine', Legal: 'NEW Ltd' }],
      mapping: { name: 'Name', legalName: 'Legal' },
      policy: 'update-matches',
      ctx,
    });
    expect(counts.updated).toBe(1);

    const [c] = await db
      .select({ legalName: companies.legalName })
      .from(companies)
      .where(and(eq(companies.portId, port.id), sql`lower(${companies.name}) = 'acme marine'`));
    expect(c!.legalName).toBe('NEW Ltd');
  });
});
