import { Injectable, NotFoundException } from '@nestjs/common';
import { TenantConnectionService } from '../../database/tenant-connection.service';
import { requireCurrentTenant } from '../../database/tenant-context';
import { AccountingVoucher } from '../../database/tenant/entities/accounting-voucher.entity';

export interface CreateVoucherDto {
  voucherType?: string;
  voucherDate?: string;
  voucherDateBs?: string;
  fiscalYear?: string;
  narration?: string;
  debitAccount?: string;
  creditAccount?: string;
  amount?: string;
  memberId?: string;
  memberName?: string;
  referenceType?: string;
  referenceId?: string;
  preparedBy?: string;
  checkedBy?: string;
  approvedBy?: string;
  officeId?: string;
  remarks?: string;
}

export type UpdateVoucherDto = Partial<CreateVoucherDto> & { status?: string };

const VOUCHER_PREFIXES: Record<string, string> = {
  CR: 'CR', CP: 'CP', JV: 'JV', BR: 'BR', BP: 'BP', CV: 'CV',
};

const CHART_OF_ACCOUNTS = [
  // Assets
  { code: '1001', name: 'Cash in Hand', nameNe: 'हातमा नगद', type: 'asset' },
  { code: '1002', name: 'Bank Account', nameNe: 'बैंक खाता', type: 'asset' },
  { code: '1101', name: 'Loan Receivable', nameNe: 'ऋण असुल', type: 'asset' },
  { code: '1102', name: 'Interest Receivable', nameNe: 'ब्याज असुल', type: 'asset' },
  { code: '1201', name: 'Fixed Assets', nameNe: 'स्थायी सम्पत्ति', type: 'asset' },
  // Liabilities
  { code: '2001', name: 'Member Savings', nameNe: 'सदस्य बचत', type: 'liability' },
  { code: '2002', name: 'Fixed Deposits', nameNe: 'मुद्दती निक्षेप', type: 'liability' },
  { code: '2003', name: 'Interest Payable', nameNe: 'ब्याज दिनुपर्ने', type: 'liability' },
  { code: '2101', name: 'Share Capital', nameNe: 'शेयर पुँजी', type: 'liability' },
  { code: '2201', name: 'Reserve Fund', nameNe: 'जगेडा कोष', type: 'liability' },
  // Income
  { code: '4001', name: 'Loan Interest Income', nameNe: 'ऋण ब्याज आम्दानी', type: 'income' },
  { code: '4002', name: 'Savings Interest Income', nameNe: 'बचत ब्याज आम्दानी', type: 'income' },
  { code: '4003', name: 'Penalty Income', nameNe: 'जरिवाना आम्दानी', type: 'income' },
  { code: '4004', name: 'Service Charge', nameNe: 'सेवा शुल्क', type: 'income' },
  { code: '4005', name: 'Other Income', nameNe: 'अन्य आम्दानी', type: 'income' },
  // Expenses
  { code: '5001', name: 'Staff Salary', nameNe: 'कर्मचारी तलब', type: 'expense' },
  { code: '5002', name: 'Rent', nameNe: 'भाडा', type: 'expense' },
  { code: '5003', name: 'Utilities', nameNe: 'उपयोगिता', type: 'expense' },
  { code: '5004', name: 'Depreciation', nameNe: 'ह्रास', type: 'expense' },
  { code: '5005', name: 'Audit Fee', nameNe: 'लेखापरीक्षण शुल्क', type: 'expense' },
  { code: '5006', name: 'Other Expense', nameNe: 'अन्य खर्च', type: 'expense' },
];

@Injectable()
export class AccountingService {
  constructor(private tenantConn: TenantConnectionService) {}

  private sub() { return requireCurrentTenant().subdomain; }
  private async repo() {
    const ds = await this.tenantConn.getTenantDataSource(this.sub());
    return ds.getRepository(AccountingVoucher);
  }

  private async nextVoucherNumber(type: string): Promise<string> {
    const r = await this.repo();
    const prefix = VOUCHER_PREFIXES[type] ?? 'VCH';
    const count = await r.count({ where: { voucherType: type } });
    return `${prefix}-${String(count + 1).padStart(6, '0')}`;
  }

  getChartOfAccounts() {
    return CHART_OF_ACCOUNTS;
  }

  async findAll(opts: { q?: string; voucherType?: string; status?: string; fiscalYear?: string; page?: number; limit?: number }) {
    const r = await this.repo();
    const { q, voucherType, status, fiscalYear, page = 1, limit = 20 } = opts;
    const qb = r.createQueryBuilder('v').orderBy('v.createdAt', 'DESC').skip((page - 1) * limit).take(limit);
    if (voucherType) qb.andWhere('v.voucherType = :voucherType', { voucherType });
    if (status) qb.andWhere('v.status = :status', { status });
    if (fiscalYear) qb.andWhere('v.fiscalYear = :fiscalYear', { fiscalYear });
    if (q) qb.andWhere('(v.voucherNumber LIKE :q OR v.narration LIKE :q OR v.memberName LIKE :q)', { q: `%${q}%` });
    const [items, total] = await qb.getManyAndCount();
    return { items, total, page, limit };
  }

  async findOne(id: string) {
    const r = await this.repo();
    const v = await r.findOne({ where: { id } });
    if (!v) throw new NotFoundException('Voucher not found');
    return v;
  }

  async create(dto: CreateVoucherDto) {
    const r = await this.repo();
    const voucherNumber = await this.nextVoucherNumber(dto.voucherType ?? 'JV');
    return r.save(r.create({ ...dto, voucherNumber, status: 'draft' } as any));
  }

  async update(id: string, dto: UpdateVoucherDto) {
    const r = await this.repo();
    const v = await this.findOne(id);
    return r.save({ ...v, ...dto });
  }

  async remove(id: string) {
    const r = await this.repo();
    const v = await this.findOne(id);
    return r.remove(v);
  }

  async getSummary(fiscalYear?: string) {
    const r = await this.repo();
    const qb = r.createQueryBuilder('v').where('v.status != :s', { s: 'cancelled' });
    if (fiscalYear) qb.andWhere('v.fiscalYear = :fy', { fy: fiscalYear });
    const vouchers = await qb.getMany();
    const byType: Record<string, number> = {};
    let totalDebit = 0;
    let totalCredit = 0;
    for (const v of vouchers) {
      const amt = Number(v.amount ?? 0);
      const t = v.voucherType ?? 'JV';
      byType[t] = (byType[t] ?? 0) + 1;
      if (['CR', 'BR', 'JV'].includes(t)) totalDebit += amt;
      else totalCredit += amt;
    }
    return { totalVouchers: vouchers.length, totalDebit, totalCredit, byType };
  }
}
