import * as plugins from './plugins.js';
import { getDb, getDbSync } from './skr.database.js';
import type { TAccountType, TSKRType, IAccountData } from './skr.types.js';

const { SmartDataDbDoc, svDb, unI, index, searchable } = plugins.smartdata;

@plugins.smartdata.Collection(() => getDbSync())
export class Account extends SmartDataDbDoc<Account, Account> {
  @unI()
  public id: string;

  @svDb()
  @index()
  public accountNumber: string;

  @svDb()
  @searchable()
  public accountName: string;

  @svDb()
  @index()
  public accountClass: number;

  @svDb()
  public accountGroup: number;

  @svDb()
  public accountSubgroup: number;

  @svDb()
  public accountType: TAccountType;

  @svDb()
  @index()
  public skrType: TSKRType;

  @svDb()
  @searchable()
  public description: string;

  @svDb()
  public vatRate: number;

  @svDb()
  public balance: number;

  @svDb()
  public debitTotal: number;

  @svDb()
  public creditTotal: number;

  @svDb()
  public isActive: boolean;

  @svDb()
  public isSystemAccount: boolean;

  @svDb()
  public createdAt: Date;

  @svDb()
  public updatedAt: Date;

  constructor(data?: Partial<IAccountData>) {
    super();

    if (data) {
      this.id = plugins.smartunique.shortId();
      this.accountNumber = data.accountNumber || '';
      this.accountName = data.accountName || '';
      this.accountClass = data.accountClass || 0;
      this.accountType = data.accountType || 'asset';
      this.skrType = data.skrType || 'SKR03';
      this.description = data.description || '';
      this.vatRate = data.vatRate || 0;
      this.isActive = data.isActive !== undefined ? data.isActive : true;

      // Parse account structure from number
      if (this.accountNumber && this.accountNumber.length === 4) {
        this.accountClass = parseInt(this.accountNumber[0]);
        this.accountGroup = parseInt(this.accountNumber[1]);
        this.accountSubgroup = parseInt(this.accountNumber[2]);
      } else {
        this.accountGroup = 0;
        this.accountSubgroup = 0;
      }

      this.balance = 0;
      this.debitTotal = 0;
      this.creditTotal = 0;
      this.isSystemAccount = true;
      this.createdAt = new Date();
      this.updatedAt = new Date();
    }
  }

  public static async createAccount(data: IAccountData): Promise<Account> {
    const account = new Account(data);
    await account.save();
    return account;
  }

  public static async getAccountByNumber(
    accountNumber: string,
    skrType: TSKRType,
  ): Promise<Account | null> {
    const account = await Account.getInstance({
      accountNumber,
      skrType,
    });
    return account;
  }

  public static async getAccountsByClass(
    accountClass: number,
    skrType: TSKRType,
  ): Promise<Account[]> {
    const accounts = await Account.getInstances({
      accountClass,
      skrType,
      isActive: true,
    });
    return accounts;
  }

  public static async getAccountsByType(
    accountType: TAccountType,
    skrType: TSKRType,
  ): Promise<Account[]> {
    const accounts = await Account.getInstances({
      accountType,
      skrType,
      isActive: true,
    });
    return accounts;
  }

  public static async searchAccounts(
    searchTerm: string,
    skrType?: TSKRType,
  ): Promise<Account[]> {
    const query: any = {};
    if (skrType) {
      query.skrType = skrType;
    }

    const accounts = await Account.getInstances(query);

    // Filter by search term
    const lowerSearchTerm = searchTerm.toLowerCase();
    return accounts.filter(
      (account) =>
        account.accountNumber.includes(searchTerm) ||
        account.accountName.toLowerCase().includes(lowerSearchTerm) ||
        account.description.toLowerCase().includes(lowerSearchTerm),
    );
  }

  public async updateBalance(
    debitAmount: number = 0,
    creditAmount: number = 0,
  ): Promise<void> {
    this.debitTotal += debitAmount;
    this.creditTotal += creditAmount;

    // Calculate balance based on account type
    switch (this.accountType) {
      case 'asset':
      case 'expense':
        // Normal debit accounts
        this.balance = this.debitTotal - this.creditTotal;
        break;
      case 'liability':
      case 'equity':
      case 'revenue':
        // Normal credit accounts
        this.balance = this.creditTotal - this.debitTotal;
        break;
    }

    this.updatedAt = new Date();
    await this.save();
  }

  public async deactivate(): Promise<void> {
    this.isActive = false;
    this.updatedAt = new Date();
    await this.save();
  }

  public async activate(): Promise<void> {
    this.isActive = true;
    this.updatedAt = new Date();
    await this.save();
  }

  public getNormalBalance(): 'debit' | 'credit' {
    switch (this.accountType) {
      case 'asset':
      case 'expense':
        return 'debit';
      case 'liability':
      case 'equity':
      case 'revenue':
        return 'credit';
    }
  }

  public async beforeSave(): Promise<void> {
    // Validate account number format
    if (!this.accountNumber || this.accountNumber.length !== 4) {
      throw new Error(
        `Invalid account number format: ${this.accountNumber}. Must be 4 digits.`,
      );
    }

    // Validate account number is numeric
    if (!/^\d{4}$/.test(this.accountNumber)) {
      throw new Error(
        `Account number must contain only digits: ${this.accountNumber}`,
      );
    }

    // Validate account class matches first digit
    const firstDigit = parseInt(this.accountNumber[0]);
    if (this.accountClass !== firstDigit) {
      throw new Error(
        `Account class ${this.accountClass} does not match account number ${this.accountNumber}`,
      );
    }

    // Validate SKR type
    if (this.skrType !== 'SKR03' && this.skrType !== 'SKR04') {
      throw new Error(`Invalid SKR type: ${this.skrType}`);
    }
  }
}
