// Mirrors the external General Ledger service's own contract (04_API_Reference_And_Scenarios.md
// in that service's repo — ERPCore does not re-document it, see docs/12-GENERAL-LEDGER-INTEGRATION.md).
// Every field here is read through ERPCore's generic proxy (`lib/api/general-ledger.ts`), which
// forwards byte-for-byte, so these types describe GL's response `data` shape directly, not an
// ERPCore DTO. As of GL's 2026-07-22 revision (docs/21-GENERAL-LEDGER-FRONTEND.md §0/§3/§4).

/** `account_type` seed rows — the block each drives in a generated account code (1000/2000/3000/4000/5000). */
export enum GlAccountTypeId {
  Asset = 1,
  Liability = 2,
  Equity = 3,
  Income = 4,
  Expense = 5,
}

export interface GlAccount {
  accountId: number
  accountCode: string
  accountName: string
  accountTypeId: GlAccountTypeId
  parentAccountId: number | null
  isControlAccount: boolean
  isPostable: boolean
  isActive: boolean
  currencyCode: string
  cashFlowCategory?: string | null
}

export interface GlAccountListResult {
  items: GlAccount[]
  totalCount: number
  page: number | null
  pageSize: number | null
}

export interface GlBudget {
  budgetId: number
  fiscalYearId: number
  name: string
  lines: unknown[]
}

export enum ReportOutputFormat {
  Json = "Json",
  Pdf = "Pdf",
  Csv = "Csv",
}

export enum ReportType {
  TrialBalance = "TrialBalance",
  BalanceSheet = "BalanceSheet",
  GeneralLedger = "GeneralLedger",
  ProfitAndLoss = "ProfitAndLoss",
  CashFlow = "CashFlow",
  BudgetVsActual = "BudgetVsActual",
  TaxSummary = "TaxSummary",
}

/** Flat as of the 2026-07-22 revision — GL dropped the hierarchy (`depth`/`indentedCode`) this report used to carry. */
export interface TrialBalanceRow {
  accountCode: string
  accountName: string
  debit: number
  credit: number
}

/** One leaf account within a Balance Sheet section — no `depth`/hierarchy anymore (2026-07-31 rework, see BalanceSheetResponse). */
export interface BalanceSheetLine {
  accountCode: string
  accountName: string
  balance: number
}

export interface BalanceSheetSection {
  lines: BalanceSheetLine[]
  total: number
}

/**
 * Confirmed shape (04_API_Reference_And_Scenarios.md, Module: Reporting, retrofit 2026-07-31) —
 * replaces the old flat recursive-rollup array (`{depth, lineItem, accountType, balance}`) entirely
 * with a classified LKAS 1 Statement of Financial Position: Non-Current/Current split for both
 * Assets and Liabilities, driven by GL's new `accounts.balance_sheet_classification` tag.
 * `equity.lines[]` always includes a synthetic `{ accountCode: "", accountName: "Current Year
 * Earnings", balance }` line (even at `0.00`). Untagged leaf accounts land in
 * `unclassifiedAssets`/`unclassifiedLiabilities` rather than being silently dropped.
 *
 * Every section is optional — same defensive posture as `CashFlowResponse`/`ProfitAndLossResponse`
 * (confirmed live: GL omits an empty section from the JSON entirely rather than sending
 * `{ lines: [], total: 0 }`), applied here pre-emptively since this exact shape hasn't been
 * live-verified against this frontend yet.
 */
export interface BalanceSheetResponse {
  asOfDate: string
  nonCurrentAssets?: BalanceSheetSection
  currentAssets?: BalanceSheetSection
  unclassifiedAssets?: BalanceSheetSection
  totalAssets: number
  equity?: BalanceSheetSection
  nonCurrentLiabilities?: BalanceSheetSection
  currentLiabilities?: BalanceSheetSection
  unclassifiedLiabilities?: BalanceSheetSection
  totalEquityAndLiabilities: number
}

export interface GeneralLedgerRow {
  entryDate: string
  journalNo: string
  accountCode: string
  accountName: string
  narration: string | null
  debitAmount: number
  creditAmount: number
  runningBalance: number
}

/** One line inside a Profit & Loss section — inferred shape (see the ProfitAndLossResponse note). */
export interface ProfitAndLossLine {
  accountCode: string
  accountName: string
  amount: number
}

export interface ProfitAndLossSection {
  lines: ProfitAndLossLine[]
  total: number
}

/**
 * Nested-sections shape (2026-07-22 rework, replaces the old flat `ProfitAndLossRow[]`). GL's own
 * reference names the sections and the two top-level totals but does not spell out each line's
 * exact field names — `ProfitAndLossLine` above is an **inferred** shape (matching every other
 * report's `accountCode`/`accountName` convention), not a confirmed contract. `unclassified` is
 * only present with lines if the Chart of Accounts has untagged Income/Expense accounts.
 *
 * Every section is optional — **confirmed live** (2026-07-31, via the identical bug on
 * `CashFlowResponse`'s list fields below): GL's serializer omits a section from the JSON
 * entirely when it has nothing to report for the period, rather than sending `{ lines: [], total: 0 }`.
 * Every consumer must optional-chain (`report.sales?.lines`), never assume presence.
 */
export interface ProfitAndLossResponse {
  sales?: ProfitAndLossSection
  costOfSales?: ProfitAndLossSection
  grossProfit: number
  otherIncome?: ProfitAndLossSection
  distributionExpenses?: ProfitAndLossSection
  administrationExpenses?: ProfitAndLossSection
  otherExpenses?: ProfitAndLossSection
  financialExpenses?: ProfitAndLossSection
  unclassified?: ProfitAndLossSection
  netProfitForPeriod: number
}

export interface CashFlowNonCashAdjustment {
  description: string
  amount: number
}

/** `changeAmount`, not `amount` — confirmed field name (04_API_Reference_And_Scenarios.md, Module: Reporting). */
export interface CashFlowWorkingCapitalChange {
  accountCode: string
  accountName: string
  direction: "Increase" | "Decrease"
  changeAmount: number
}

/** One line inside an Investing/Financing section — GL's reference confirms `lines[]` exists but not
 * this line's own field names; `{description, amount}` here matches every other report's line-item
 * convention but is not verified verbatim. */
export interface CashFlowActivityLine {
  description: string
  amount: number
}

export interface CashFlowOperatingActivities {
  profitForPeriod: number
  nonCashAdjustments: CashFlowNonCashAdjustment[]
  workingCapitalChanges: CashFlowWorkingCapitalChange[]
  netCashFromOperatingActivities: number
}

export interface CashFlowInvestingActivities {
  lines: CashFlowActivityLine[]
  netCashFromInvestingActivities: number
}

export interface CashFlowFinancingActivities {
  lines: CashFlowActivityLine[]
  netCashFromFinancingActivities: number
}

/**
 * Confirmed shape (04_API_Reference_And_Scenarios.md, Module: Reporting — GL's own API reference,
 * not inferred). **Everything nests under `operatingActivities`** — the previous version of this
 * type had `netEarnings`/`nonCashAdjustments`/`workingCapitalChanges`/`netCashFromOperations` as
 * flat top-level fields, which was wrong and caused a live runtime crash (`Cannot read properties
 * of undefined (reading 'map')` on `nonCashAdjustments` — it was never at the top level to begin
 * with). `investingActivities`/`financingActivities` each have their own differently-named total
 * field (`netCashFromInvestingActivities`/`netCashFromFinancingActivities`), not a shared `total`.
 * `openingCashBalance`/`closingCashBalance` are returned for validation but deliberately not
 * rendered on screen — GL's own PDF/CSV doesn't print them either.
 */
export interface CashFlowResponse {
  periodStart: string
  periodEnd: string
  operatingActivities: CashFlowOperatingActivities
  investingActivities: CashFlowInvestingActivities
  financingActivities: CashFlowFinancingActivities
  netIncreaseDecreaseInCash: number
  openingCashBalance: number
  closingCashBalance: number
}

export interface BudgetVsActualRow {
  budgetLineId: number
  accountCode: string
  accountName: string
  periodId: number
  budgetedAmount: number
  actualAmount: number
  variance: number
}

/**
 * Income Tax Computation (2026-07-22 redesign). Confirmed shape (04_API_Reference_And_Scenarios.md,
 * Module: Reporting) — not inferred. The previous version of this type was missing
 * `nonDeductibleExpenses`, `corporateIncomeTax`, `apitCredit`, `whtCredit`, and
 * `quarterlyTaxPayments` entirely, which meant the Tax Report screen was silently dropping real
 * GL-computed figures rather than a made-up guess just being wrong. Full row order,
 * `profitBeforeTax` → `balanceTaxPayable`, with bold reconciliation checkpoints at
 * `adjustedBusinessProfit`/`assessableIncome`/`taxableIncome`/`grossTaxLiability`/the final total.
 */
export interface TaxSummaryResponse {
  periodStart: string
  periodEnd: string
  profitBeforeTax: number
  nonDeductibleExpenses: number
  allowableDeductions: number
  adjustedBusinessProfit: number
  otherTaxableIncome: number
  assessableIncome: number
  qualifyingPaymentsReliefs: number
  taxableIncome: number
  taxRatePercent: number
  corporateIncomeTax: number
  surchargeAmount: number
  grossTaxLiability: number
  apitCredit: number
  whtCredit: number
  quarterlyTaxPayments: number
  balanceTaxPayable: number
}

export interface TaxSummaryParams {
  periodStart: string
  periodEnd: string
  allowableDeductions?: number
  otherTaxableIncome?: number
  qualifyingPaymentsReliefs?: number
  surchargeAmount?: number
  taxRateOverride?: number
}

/** `outputFormat=Pdf`/`Csv` response shape — base64-encoded bytes inside the normal envelope either way. */
export interface GlFilePayload {
  fileName: string
  contentType: string
  contentBase64: string
}

/** Cash and Bank are two separate GL tables/endpoints (2026-07-22 rework) — this discriminates the unified list row and the create-form toggle, not a database column on either side. */
export enum CashBankAccountType {
  Cash = "Cash",
  Bank = "Bank",
}

/**
 * `GET /bank-accounts?accountType=Cash|Bank|Both` row shape (2026-07-22 rework) — GL's own reference
 * documents this explicitly (`docs/21` §4), unlike the old single-table `BankAccount` shape it
 * replaces, which was inferred. `bankName` is `null` on `Cash` rows, `cashAccountTypeName` is `null`
 * on `Bank` rows.
 */
export interface CashAndBankAccountDto {
  accountType: CashBankAccountType
  accountId: number
  accountName: string
  bankName: string | null
  cashAccountTypeName: string | null
  accountNumber: string | null
  glAccountId: number
  /** The underlying GL account's business code (added GL Phase 38, 2026-08-12). */
  glAccountCode: string
  currencyCode: string
  createdAt: string
}

/**
 * Confirmed (04_API_Reference_And_Scenarios.md, Module: Bank, retrofit 2026-07-31) —
 * `glAccountCode` was removed from this request entirely. The backing GL account is now always
 * auto-created server-side (a root "Bank" header account is found-or-created, then a postable leaf
 * named after `accountName` is created under it) — the caller never selects or supplies a GL account.
 */
export interface CreateBankAccountRequest {
  accountName: string
  bankName?: string | null
  accountNumber?: string | null
  currencyCode?: string
}

/**
 * Confirmed (retrofit 2026-07-31) — same `glAccountCode` removal as `CreateBankAccountRequest`, plus
 * one more auto-created level: a root "Cash" header, then a per-`cashAccountTypeName` header (created
 * once, reused thereafter), then a postable leaf named after `accountName`.
 */
export interface CreateCashAccountRequest {
  accountName: string
  cashAccountTypeName: string
  accountNumber?: string | null
  currencyCode?: string
}

/**
 * `POST /bank-accounts` / `POST /cash-accounts` response (retrofit 2026-07-31) — since the GL account
 * is now auto-created rather than caller-supplied, the created leaf account (nested under its
 * auto-created/reused header via `parentAccount`) is returned under `glAccount` so the caller can see
 * exactly what was generated. `glAccount`/`glAccountId` are confirmed from the reference doc; the
 * other fields are inferred (they mirror the create request's own fields plus an id, following this
 * project's usual `<entity>` response convention).
 */
export interface CreateCashOrBankAccountResponse {
  accountName: string
  bankName?: string | null
  cashAccountTypeName?: string | null
  accountNumber?: string | null
  currencyCode: string
  glAccountId: number
  glAccount: GlAccount & { parentAccount?: GlAccount | null }
}

/** `GET /cash-account-types` row — flat reference list (seeded Petty Cash / Till Cash / Safe Cash / Cash in Transit, grows over time via on-the-fly creation from the create form). */
export interface CashAccountType {
  cashAccountTypeId: number
  name: string
}

/** Shared `{ items, totalCount, page, pageSize }` list envelope used by every Cheque Management list endpoint. */
export interface GlPagedResult<T> {
  items: T[]
  totalCount: number
  page: number | null
  pageSize: number | null
}

// ---------------------------------------------------------------------------
// Cheque Management (new GL module, added 2026-07-30, beyond the original plan).
// Purely operational tracking — no endpoint here ever creates/touches a journal entry itself.
// `branchId`/`companyId`/`payeeId`/`voucherId`/`referenceId` are documented as deliberately
// "loose references" (plain unvalidated numbers) — no Branch/Company/Customer/Supplier table
// exists in this GL service for them to point at, so the frontend takes them as free-entry
// numbers rather than picker dropdowns, matching GL's own stated design.
// ---------------------------------------------------------------------------

export enum PayeeType {
  Supplier = "Supplier",
  Customer = "Customer",
  Employee = "Employee",
  Other = "Other",
}

export enum ReceivedFromType {
  Customer = "Customer",
  Supplier = "Supplier",
  Other = "Other",
}

export enum ChequeBookStatus {
  Active = "Active",
  Completed = "Completed",
  Cancelled = "Cancelled",
}

export enum ChequePageIssueStatus {
  Unused = "Unused",
  Issued = "Issued",
  Cleared = "Cleared",
  Bounced = "Bounced",
  Cancelled = "Cancelled",
  Void = "Void",
}

/** `PUT /cheque-pages/{chequeNo}/status`'s `action` values. */
export enum ChequePageStatusAction {
  Clear = "Clear",
  Bounce = "Bounce",
  Cancel = "Cancel",
  Void = "Void",
}

export enum ReceivedChequeStatus {
  Received = "Received",
  Deposited = "Deposited",
  Cleared = "Cleared",
  Returned = "Returned",
  Cancelled = "Cancelled",
}

/** `PUT /received-cheques/{id}/status`'s `action` values. */
export enum ReceivedChequeStatusAction {
  Deposit = "Deposit",
  Clear = "Clear",
  Return = "Return",
  Cancel = "Cancel",
}

/**
 * Confirmed (`06_Enums_Reference.md`, user-supplied) — GL has **no global `JsonStringEnumConverter`**
 * registered. That's documented there as a request-body binding gotcha (a JSON-body enum field must
 * be sent as its string name, parsed server-side via `Enum.TryParse`), but the same missing converter
 * also governs the other direction: every one of these five fields is a real enum-typed property on
 * GL's own response DTO (backed by an `integer` DB column, per that doc's "Persisted enums" table),
 * so with no converter registered, GL's JSON response serializes each one as its **raw integer**
 * (`1`/`2`/`3`/...), not the name — confirmed live by the user ("most status has integer numbers").
 * Request bodies/query-string filters are unaffected and still take the string name as before (a
 * JSON-body enum field is independently declared `string` server-side, and query-string enum
 * binding parses names natively) — only inbound response values need translating. These maps do
 * that translation, keyed by the exact integer values `06_Enums_Reference.md` documents; applied in
 * `lib/api/general-ledger.ts` immediately after each GL call returns, so every consumer of
 * `ChequeBook`/`ChequePage`/`ReceivedCheque` in this frontend keeps working with the same string
 * enum values as before and never has to know GL sent a number.
 */
export const CHEQUE_BOOK_STATUS_BY_CODE: Record<number, ChequeBookStatus> = {
  1: ChequeBookStatus.Active,
  2: ChequeBookStatus.Completed,
  3: ChequeBookStatus.Cancelled,
}

export const CHEQUE_PAGE_ISSUE_STATUS_BY_CODE: Record<number, ChequePageIssueStatus> = {
  1: ChequePageIssueStatus.Unused,
  2: ChequePageIssueStatus.Issued,
  3: ChequePageIssueStatus.Cleared,
  4: ChequePageIssueStatus.Bounced,
  5: ChequePageIssueStatus.Cancelled,
  6: ChequePageIssueStatus.Void,
}

/** `ChequePage.payeeType` is nullable — only set once a page is issued (`06_Enums_Reference.md`). */
export const PAYEE_TYPE_BY_CODE: Record<number, PayeeType> = {
  1: PayeeType.Supplier,
  2: PayeeType.Customer,
  3: PayeeType.Employee,
  4: PayeeType.Other,
}

export const RECEIVED_FROM_TYPE_BY_CODE: Record<number, ReceivedFromType> = {
  1: ReceivedFromType.Customer,
  2: ReceivedFromType.Supplier,
  3: ReceivedFromType.Other,
}

export const RECEIVED_CHEQUE_STATUS_BY_CODE: Record<number, ReceivedChequeStatus> = {
  1: ReceivedChequeStatus.Received,
  2: ReceivedChequeStatus.Deposited,
  3: ReceivedChequeStatus.Cleared,
  4: ReceivedChequeStatus.Returned,
  5: ReceivedChequeStatus.Cancelled,
}

/**
 * A single leaf of a Cheque Book. GL's own reference confirms every field named in the `issue`
 * request body plus `issueStatus`/`printedAt`/`clearedDate`/`clearedByBank`/`cancelReason` in
 * prose — this type is built from those, not a guessed shape. `chequeNo` (not a numeric id) is
 * the documented identifying value for `GET/PUT /cheque-pages/{chequeNo}`, so it's used as the
 * key/URL param throughout rather than an unconfirmed `chequePageId`.
 */
export interface ChequePage {
  chequeNo: string
  chequeBookNo?: string
  issueStatus: ChequePageIssueStatus
  payeeType: PayeeType | null
  payeeId: number | null
  payeeName: string | null
  issueDate: string | null
  amount: number | null
  currencyCode: string | null
  voucherId: number | null
  referenceNo: string | null
  purpose: string | null
  isCrossCheque: boolean | null
  isAccountPayee: boolean | null
  isPostDated: boolean | null
  notes: string | null
  printedBy: string | null
  printedAt: string | null
  clearedDate: string | null
  clearedByBank: boolean | null
  cancelReason: string | null
}

/**
 * A cheque book issued from this company's own supply. `chequeBookNo` (caller-supplied, unique)
 * is the documented identifying value for `GET /cheque-books/{chequeBookNo}`, used as the
 * key/URL param throughout. `pages[]` is only populated when fetched with `?expand=pages`.
 */
export interface ChequeBook {
  chequeBookNo: string
  branchId: number
  bankAccountId: number
  startChequeNo: string
  endChequeNo: string
  totalLeaves: number
  receivedDate: string
  description: string | null
  createdBy: string | null
  status: ChequeBookStatus
  pages: ChequePage[]
}

export interface CreateChequeBookRequest {
  branchId: number
  bankAccountId: number
  chequeBookNo: string
  startChequeNo: string
  endChequeNo: string
  totalLeaves: number
  receivedDate: string
  description?: string
  createdBy?: string
}

export interface IssueChequePageRequest {
  payeeType: PayeeType
  payeeId?: number
  payeeName: string
  issueDate: string
  amount: number
  currencyCode?: string
  voucherId?: number
  referenceNo?: string
  purpose?: string
  isCrossCheque?: boolean
  isAccountPayee?: boolean
  isPostDated?: boolean
  notes?: string
  printedBy?: string
}

export interface UpdateChequePageStatusRequest {
  action: ChequePageStatusAction
  /** Required only for `action: "Clear"`. */
  clearedDate?: string
  /** Required only for `action: "Cancel"`. */
  cancelReason?: string
  performedBy?: string
}

/**
 * A cheque received from a customer/supplier/other party — deliberately has no link to a
 * `ChequeBook` (it isn't one of this company's own). GL's reference uses a numeric `{id}` in the
 * URL for `GET/PUT /received-cheques/{id}` without spelling out the JSON field's exact name —
 * `receivedChequeId` follows this project's consistent `<entity>Id` convention (e.g. `assetId`,
 * `disposalId`), not a confirmed literal.
 */
export interface ReceivedCheque {
  receivedChequeId: number
  companyId: number
  branchId: number | null
  receivedFromType: ReceivedFromType
  receivedFromId: number | null
  receivedFromName: string
  drawerBankName: string | null
  drawerBankBranch: string | null
  accountHolderName: string | null
  chequeNo: string
  chequeDate: string
  amount: number
  receivedDate: string
  referenceType: string | null
  referenceId: number | null
  notes: string | null
  createdBy: string | null
  status: ReceivedChequeStatus
  depositBankAccountId: number | null
  depositDate: string | null
}

export interface CreateReceivedChequeRequest {
  companyId: number
  branchId?: number
  receivedFromType: ReceivedFromType
  receivedFromId?: number
  receivedFromName: string
  drawerBankName?: string
  drawerBankBranch?: string
  accountHolderName?: string
  chequeNo: string
  chequeDate: string
  amount: number
  receivedDate: string
  referenceType?: string
  referenceId?: number
  notes?: string
  createdBy?: string
}

export interface UpdateReceivedChequeStatusRequest {
  action: ReceivedChequeStatusAction
  /** Required only for `action: "Deposit"`. */
  depositBankAccountId?: number
  /** Required only for `action: "Deposit"`. */
  depositDate?: string
  notes?: string
  performedBy?: string
}
