import type { Transaction } from "../generated/kysely-tailordb";
import {
  BudgetNotFoundError,
  InvalidStateError,
  InvalidCostElementError,
  AccountingPeriodNotFoundError,
  InvalidPlannedAmountError,
  DuplicateLineItemError,
} from "../lib/errors.generated";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface CreateBudgetLineItemInput {
  budgetId: string;
  costElementId: string;
  accountingPeriodId: string;
  plannedAmount: number;
}

export async function run(db: Transaction, input: CreateBudgetLineItemInput, ctx: CommandContext) {
  void ctx;

  // Validate budget exists and is DRAFT
  const budget = await db
    .selectFrom("Budget")
    .where("id", "=", input.budgetId)
    .selectAll()
    .forUpdate()
    .executeTakeFirst();

  if (!budget) {
    return err(new BudgetNotFoundError(input.budgetId));
  }

  if (budget.status !== "DRAFT") {
    return err(new InvalidStateError(input.budgetId));
  }

  // Validate cost element exists, is PRIMARY, and ACTIVE
  const costElement = await db
    .selectFrom("CostElement")
    .where("id", "=", input.costElementId)
    .selectAll()
    .executeTakeFirst();

  if (costElement?.type !== "PRIMARY" || costElement.status !== "ACTIVE") {
    return err(new InvalidCostElementError(input.costElementId));
  }

  // Validate accounting period exists
  const accountingPeriod = await db
    .selectFrom("AccountingPeriod" as never)
    .where("id" as never, "=", input.accountingPeriodId as never)
    .selectAll()
    .executeTakeFirst();

  if (!accountingPeriod) {
    return err(new AccountingPeriodNotFoundError(input.accountingPeriodId));
  }

  // Validate planned amount is positive
  if (input.plannedAmount <= 0) {
    return err(new InvalidPlannedAmountError(input.budgetId));
  }

  // Check for duplicate line item (same cost element + period)
  const duplicate = await db
    .selectFrom("BudgetLineItem")
    .where("budgetId", "=", input.budgetId)
    .where("costElementId", "=", input.costElementId)
    .where("accountingPeriodId", "=", input.accountingPeriodId)
    .selectAll()
    .executeTakeFirst();

  if (duplicate) {
    return err(new DuplicateLineItemError(input.budgetId));
  }

  // Create the line item
  const lineItem = await db
    .insertInto("BudgetLineItem")
    .values({
      budgetId: input.budgetId,
      costElementId: input.costElementId,
      accountingPeriodId: input.accountingPeriodId,
      glAccountId: costElement.glAccountId ?? null, // Derive from cost element
      plannedAmount: input.plannedAmount,
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  // Update budget totalAmount
  await db
    .updateTable("Budget")
    .set({
      totalAmount: budget.totalAmount + input.plannedAmount,
      updatedAt: new Date(),
    })
    .where("id", "=", input.budgetId)
    .execute();

  return ok({ budgetLineItem: lineItem! });
}
