import { type EmptyFields, type FieldsToInsertable } from "@tailor-platform/erp-kit/core";
import type { TailorAnyDBField } from "@tailor-platform/sdk";
import type { defineModule as defineCoaManagementModule } from "../coa-management";
import type { defineModule as defineOrganizationModule } from "../organization";
import { cancelJournalEntry } from "./command/cancelJournalEntry.generated";
import { closePeriod } from "./command/closePeriod.generated";
import { createAccountingPeriod } from "./command/createAccountingPeriod.generated";
import { createFiscalYear } from "./command/createFiscalYear.generated";
import { createJournalEntry } from "./command/createJournalEntry.generated";
import { deleteAccountingPeriod } from "./command/deleteAccountingPeriod.generated";
import { deleteFiscalYear } from "./command/deleteFiscalYear.generated";
import { openPeriod } from "./command/openPeriod.generated";
import { permanentlyClosePeriod } from "./command/permanentlyClosePeriod.generated";
import { postJournalEntry } from "./command/postJournalEntry.generated";
import { reopenPeriod } from "./command/reopenPeriod.generated";
import { reverseJournalEntry } from "./command/reverseJournalEntry.generated";
import { updateFiscalYear } from "./command/updateFiscalYear.generated";
import { updateJournalEntry } from "./command/updateJournalEntry.generated";
import {
  createAccountingPeriodType,
  type CreateAccountingPeriodTypeParams,
} from "./db/accountingPeriod";
import { createFiscalYearType, type CreateFiscalYearTypeParams } from "./db/fiscalYear";
import { createJournalEntryType, type CreateJournalEntryTypeParams } from "./db/journalEntry";
import { createJournalLineType, type CreateJournalLineTypeParams } from "./db/journalLine";
import { getAccountingPeriod } from "./query/getAccountingPeriod.generated";
import { getFiscalYear } from "./query/getFiscalYear.generated";
import { getJournalEntry } from "./query/getJournalEntry.generated";
import { getPeriodByDate } from "./query/getPeriodByDate.generated";
import { listAccountingPeriods } from "./query/listAccountingPeriods.generated";
import { listFiscalYears } from "./query/listFiscalYears.generated";
import { listJournalEntries } from "./query/listJournalEntries.generated";

type OrganizationModule = ReturnType<typeof defineOrganizationModule>;
type OrganizationDB = OrganizationModule["db"];
export type OrganizationQueries = OrganizationModule["queries"];
type CoaManagementModule = ReturnType<typeof defineCoaManagementModule>;
type CoaManagementDB = CoaManagementModule["db"];
export type CoaManagementQueries = CoaManagementModule["queries"];

export interface DefineModuleParams<
  FyF extends Record<string, TailorAnyDBField> = EmptyFields,
  ApF extends Record<string, TailorAnyDBField> = EmptyFields,
  JeF extends Record<string, TailorAnyDBField> = EmptyFields,
  JlF extends Record<string, TailorAnyDBField> = EmptyFields,
> {
  fiscalYear?: CreateFiscalYearTypeParams<FyF>;
  accountingPeriod?: CreateAccountingPeriodTypeParams<ApF>;
  journalEntry?: CreateJournalEntryTypeParams<JeF>;
  journalLine?: CreateJournalLineTypeParams<JlF>;
  organization: {
    db: {
      company: OrganizationDB["company"];
    };
    queries: Pick<OrganizationQueries, "getCompany">;
  };
  coaManagement: {
    db: {
      account: CoaManagementDB["account"];
    };
    queries: Pick<CoaManagementQueries, "listAccounts">;
  };
}

/* @__NO_SIDE_EFFECTS__ */
export const defineModule = <
  const FyF extends Record<string, TailorAnyDBField> = EmptyFields,
  const ApF extends Record<string, TailorAnyDBField> = EmptyFields,
  const JeF extends Record<string, TailorAnyDBField> = EmptyFields,
  const JlF extends Record<string, TailorAnyDBField> = EmptyFields,
>(
  params: DefineModuleParams<FyF, ApF, JeF, JlF>,
) => {
  const fiscalYearDb = createFiscalYearType({
    ...params.fiscalYear,
    companyType: params.organization.db.company,
  });
  const accountingPeriodDb = createAccountingPeriodType({
    ...params.accountingPeriod,
    companyType: params.organization.db.company,
  });
  const journalEntryDb = createJournalEntryType({
    ...params.journalEntry,
    companyType: params.organization.db.company,
  });
  const journalLineDb = createJournalLineType({
    ...params.journalLine,
    accountType: params.coaManagement.db.account,
  });

  return {
    db: {
      fiscalYear: fiscalYearDb,
      accountingPeriod: accountingPeriodDb,
      journalEntry: journalEntryDb,
      journalLine: journalLineDb,
    },
    commands: {
      cancelJournalEntry: cancelJournalEntry(),
      closePeriod: closePeriod(),
      createAccountingPeriod: createAccountingPeriod<FieldsToInsertable<ApF>>(),
      createFiscalYear: createFiscalYear<FieldsToInsertable<FyF>>(params.organization.queries),
      createJournalEntry: createJournalEntry<FieldsToInsertable<JeF>, FieldsToInsertable<JlF>>(
        params.organization.queries,
        params.coaManagement.queries,
      ),
      deleteAccountingPeriod: deleteAccountingPeriod(),
      deleteFiscalYear: deleteFiscalYear(),
      openPeriod: openPeriod(),
      permanentlyClosePeriod: permanentlyClosePeriod(),
      postJournalEntry: postJournalEntry(params.coaManagement.queries),
      reopenPeriod: reopenPeriod(),
      reverseJournalEntry: reverseJournalEntry(),
      updateFiscalYear: updateFiscalYear<FieldsToInsertable<FyF>>(),
      updateJournalEntry: updateJournalEntry<FieldsToInsertable<JeF>, FieldsToInsertable<JlF>>(
        params.coaManagement.queries,
      ),
    },
    queries: {
      getAccountingPeriod,
      getFiscalYear,
      getJournalEntry,
      getPeriodByDate,
      listAccountingPeriods,
      listFiscalYears,
      listJournalEntries,
    },
  };
};
