import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  PeriodCloseNotFoundError,
  InvalidStatusTransitionError,
  TrialBalanceNotFoundError,
} from "../lib/errors.generated";

export interface GeneratePreliminaryStatementsInput {
  id: string;
}

/**
 * Function: generatePreliminaryStatements
 *
 * Generates preliminary Balance Sheet and P&L statements, transitions
 * PeriodClose from TRIAL_BALANCE_VERIFIED to UNDER_REVIEW. Statements
 * are DRAFT for management review. Requires TRIAL_BALANCE_VERIFIED status.
 * Verifies trial balance remains balanced. Records operator's userId.
 */
export async function run(
  db: Transaction,
  input: GeneratePreliminaryStatementsInput,
  ctx: CommandContext,
) {
  const { id } = input;

  // 1. Find PeriodClose record
  const periodClose = await db
    .selectFrom("PeriodClose")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!periodClose) {
    return err(new PeriodCloseNotFoundError(id));
  }

  // 2. Validate status is TRIAL_BALANCE_VERIFIED
  if (periodClose.status !== "TRIAL_BALANCE_VERIFIED") {
    return err(new InvalidStatusTransitionError(id));
  }

  // 3. Find the trial balance and verify it remains balanced
  const trialBalance = await db
    .selectFrom("TrialBalance")
    .selectAll()
    .where("id", "=", periodClose.trialBalanceId!)
    .executeTakeFirst();

  if (!trialBalance?.isBalanced) {
    return err(new TrialBalanceNotFoundError(id));
  }

  // 4. Find accounting period for company context
  const accountingPeriod = await db
    .selectFrom("AccountingPeriod")
    .selectAll()
    .where("id", "=", periodClose.accountingPeriodId)
    .executeTakeFirst();

  // 5. Find active CoA for the company
  const chartOfAccounts = await db
    .selectFrom("ChartOfAccounts")
    .selectAll()
    .where("companyId", "=", accountingPeriod!.companyId)
    .where("status", "=", "ACTIVE")
    .executeTakeFirst();

  // 6. Generate two DRAFT FinancialStatement records
  const now = new Date();

  const balanceSheet = await db
    .insertInto("FinancialStatement")
    .values({
      companyId: accountingPeriod!.companyId,
      chartOfAccountsId: chartOfAccounts!.id,
      trialBalanceId: trialBalance.id,
      comparativePeriodTrialBalanceId: null,
      statementType: "BALANCE_SHEET",
      status: "DRAFT",
      reportingDate: accountingPeriod!.endDate,
      startDate: null,
      endDate: null,
      cashFlowMethod: null,
      generatedAt: now,
      finalizedAt: null,
      createdAt: now,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  const profitAndLoss = await db
    .insertInto("FinancialStatement")
    .values({
      companyId: accountingPeriod!.companyId,
      chartOfAccountsId: chartOfAccounts!.id,
      trialBalanceId: trialBalance.id,
      comparativePeriodTrialBalanceId: null,
      statementType: "PROFIT_AND_LOSS",
      status: "DRAFT",
      reportingDate: accountingPeriod!.endDate,
      startDate: accountingPeriod!.startDate,
      endDate: accountingPeriod!.endDate,
      cashFlowMethod: null,
      generatedAt: now,
      finalizedAt: null,
      createdAt: now,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  // 7. Update PeriodClose to UNDER_REVIEW
  const updated = await db
    .updateTable("PeriodClose")
    .set({
      status: "UNDER_REVIEW",
      completionPercentage: 75,
      preliminaryStatementIds: JSON.stringify([balanceSheet.id, profitAndLoss.id]),
      updatedAt: now,
    })
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  // 8. Emit audit trail
  const auditTrail = [
    {
      type: "STATUS_TRANSITION",
      from: "TRIAL_BALANCE_VERIFIED",
      to: "UNDER_REVIEW",
      actorId: ctx.actorId,
      timestamp: now,
      statementIds: [balanceSheet.id, profitAndLoss.id],
    },
  ];

  return ok({ periodClose: updated, statements: [balanceSheet, profitAndLoss], auditTrail });
}
