import {
  db,
  type TailorAnyDBField,
  type TailorAnyDBType,
  unsafeAllowAllGqlPermission,
  unsafeAllowAllTypePermission,
} from "@tailor-platform/sdk";
import { currency as currencyStub } from "../lib/_db_deps";
import { chartOfAccounts as chartOfAccountsStub } from "./chartOfAccounts";
import { accountGroup as accountGroupStub } from "./accountGroup";

export const BASE_ACCOUNT_STATUSES = ["DRAFT", "ACTIVE", "INACTIVE"] as const;

export const ACCOUNT_TYPES = ["ASSET", "LIABILITY", "EQUITY", "REVENUE", "EXPENSE"] as const;

export const ACCOUNT_CLASSIFICATIONS = [
  "REGULAR",
  "RECEIVABLE",
  "PAYABLE",
  "BANK_CASH",
  "FIXED_ASSET",
  "RETAINED_EARNINGS",
  "NON_POSTING",
] as const;

export interface CreateAccountTypeParams<F extends Record<string, TailorAnyDBField>> {
  fields?: F;
  additionalStatuses?: string[];
  chartOfAccountsType?: TailorAnyDBType;
  accountGroupType?: TailorAnyDBType;
  currencyType?: TailorAnyDBType;
}

export function createAccountType<const F extends Record<string, TailorAnyDBField>>(
  params: CreateAccountTypeParams<F>,
) {
  const statuses = [...BASE_ACCOUNT_STATUSES, ...(params.additionalStatuses ?? [])] as [
    string,
    ...string[],
  ];

  return db
    .type("Account", {
      code: db.string().description("Structured numeric account code, unique within the CoA"),
      name: db.string().description("Descriptive name of the GL account"),
      accountType: db
        .enum(["ASSET", "LIABILITY", "EQUITY", "REVENUE", "EXPENSE"])
        .description("Account type: ASSET, LIABILITY, EQUITY, REVENUE, or EXPENSE"),
      classification: db
        .enum([
          "REGULAR",
          "RECEIVABLE",
          "PAYABLE",
          "BANK_CASH",
          "FIXED_ASSET",
          "RETAINED_EARNINGS",
          "NON_POSTING",
        ])
        .description("Operational classification of the account"),
      chartOfAccountsId: db
        .uuid()
        .relation({
          type: "n-1",
          toward: { type: params.chartOfAccountsType ?? chartOfAccountsStub },
          backward: "accounts",
        })
        .description("Foreign key to the parent ChartOfAccounts"),
      accountGroupId: db
        .uuid({ optional: true })
        .relation({
          type: "n-1",
          toward: { type: params.accountGroupType ?? accountGroupStub },
          backward: "accounts",
        })
        .description("Optional foreign key to AccountGroup for hierarchical organization"),
      currencyRestriction: db
        .uuid({ optional: true })
        .relation({
          type: "n-1",
          toward: { type: params.currencyType ?? currencyStub },
          backward: "accounts",
        })
        .description(
          "Optional currency restriction; when set, only postings in this currency are accepted",
        ),
      reconciliation: db.bool().description("Whether this account requires reconciliation"),
      defaultTaxCode: db
        .string({ optional: true })
        .description(
          "Optional default tax code (free-text, future migration to validated reference)",
        ),
      tags: db
        .string({ optional: true })
        .description("Comma-separated string labels for categorization (e.g. 'tag1,tag2')"),
      successorAccountId: db
        .uuid({ optional: true })
        .relation({
          type: "n-1",
          toward: { type: "self" },
          backward: "predecessorAccounts",
        })
        .description("Optional successor account mapped during deactivation"),
      hasPostedTransactions: db
        .bool()
        .description("Flag indicating whether the account has posted transactions"),
      status: db.enum(statuses).description("Lifecycle status: DRAFT, ACTIVE, INACTIVE"),
      ...((params.fields ?? {}) as F),
      ...db.fields.timestamps(),
    })
    .indexes({
      fields: ["chartOfAccountsId", "code"],
      unique: true,
      name: "account_coa_code_unique_idx",
    })
    .permission(unsafeAllowAllTypePermission)
    .gqlPermission(unsafeAllowAllGqlPermission);
}

export const account = createAccountType({});
