import type { Transaction } from "../generated/kysely-tailordb";
import {
  CompanyNotFoundError,
  DuplicateCostElementCodeError,
  InvalidCostElementTypeError,
  InvalidCategoryForTypeError,
  GlAccountRequiredError,
  GlAccountNotFoundError,
  GlAccountNotAllowedError,
  DuplicateGlAccountReferenceError,
  InvalidValidityPeriodError,
} from "../lib/errors.generated";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

const PRIMARY_CATEGORIES = ["overhead", "material", "labor", "depreciation"];
const SECONDARY_CATEGORIES = [
  "assessment",
  "distribution",
  "internal_activity_allocation",
  "settlement",
];

export interface CreateCostElementInput {
  companyId: string;
  code: string;
  name: string;
  type: string;
  category: string;
  glAccountId?: string | null;
  validFrom?: Date | null;
  validTo?: Date | null;
  description?: string | null;
}

/** Command: createCostElement
 * Establishes a new cost element in DRAFT status within the management accounting module.
 */
export async function run(db: Transaction, input: CreateCostElementInput, ctx: CommandContext) {
  void ctx;

  // Validate company exists and is ACTIVE
  const company = await db
    .selectFrom("Company" as never)
    .where("id" as never, "=", input.companyId as never)
    .selectAll()
    .executeTakeFirst();

  if (!company || (company as Record<string, unknown>).status !== "ACTIVE") {
    return err(new CompanyNotFoundError(input.companyId));
  }

  // Validate code uniqueness within company
  const existingCode = await db
    .selectFrom("CostElement")
    .where("companyId", "=", input.companyId)
    .where("code", "=", input.code)
    .selectAll()
    .executeTakeFirst();

  if (existingCode) {
    return err(new DuplicateCostElementCodeError(input.code));
  }

  // Validate type
  if (input.type !== "PRIMARY" && input.type !== "SECONDARY") {
    return err(new InvalidCostElementTypeError(input.type));
  }

  // Validate category matches type
  if (input.type === "PRIMARY" && !PRIMARY_CATEGORIES.includes(input.category)) {
    return err(new InvalidCategoryForTypeError(input.category));
  }
  if (input.type === "SECONDARY" && !SECONDARY_CATEGORIES.includes(input.category)) {
    return err(new InvalidCategoryForTypeError(input.category));
  }

  // GL account validation for PRIMARY
  if (input.type === "PRIMARY") {
    if (!input.glAccountId) {
      return err(new GlAccountRequiredError(input.code));
    }

    // Validate GL account exists and is active in an active CoA
    const account = await db
      .selectFrom("Account" as never)
      .where("id" as never, "=", input.glAccountId as never)
      .selectAll()
      .executeTakeFirst();

    if (!account || (account as Record<string, unknown>).status !== "ACTIVE") {
      return err(new GlAccountNotFoundError(input.glAccountId));
    }

    const chartOfAccounts = await db
      .selectFrom("ChartOfAccounts" as never)
      .where("id" as never, "=", (account as Record<string, unknown>).chartOfAccountsId as never)
      .selectAll()
      .executeTakeFirst();

    if (!chartOfAccounts || (chartOfAccounts as Record<string, unknown>).status !== "ACTIVE") {
      return err(new GlAccountNotFoundError(input.glAccountId));
    }

    // Check no other PRIMARY element references same GL account in same company
    const duplicateGlRef = await db
      .selectFrom("CostElement")
      .where("companyId", "=", input.companyId)
      .where("glAccountId", "=", input.glAccountId)
      .where("type", "=", "PRIMARY")
      .selectAll()
      .executeTakeFirst();

    if (duplicateGlRef) {
      return err(new DuplicateGlAccountReferenceError(input.glAccountId));
    }
  }

  // GL account validation for SECONDARY - must NOT provide glAccountId
  if (input.type === "SECONDARY" && input.glAccountId) {
    return err(new GlAccountNotAllowedError(input.code));
  }

  // Validate validity dates
  if (input.validFrom && input.validTo && input.validFrom > input.validTo) {
    return err(new InvalidValidityPeriodError(input.code));
  }

  // Create cost element in DRAFT status
  const costElement = await db
    .insertInto("CostElement")
    .values({
      companyId: input.companyId,
      code: input.code,
      name: input.name,
      type: input.type,
      category: input.category,
      glAccountId: input.glAccountId ?? null,
      validFrom: input.validFrom ?? null,
      validTo: input.validTo ?? null,
      description: input.description ?? null,
      status: "DRAFT",
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ costElement: costElement! });
}
