import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import {
  TrustAccount, TrustAccountDocument,
  TrustTransaction, TrustTransactionDocument,
  TrustTxType,
} from '../common/schemas'
import { CreateTrustTransactionDto, ReconcileDto } from './dto/create-trust-transaction.dto'

// Direction of each transaction type relative to client balance
const CREDIT_TYPES: TrustTxType[] = ['receipt', 'interest']  // increase balance
const DEBIT_TYPES:  TrustTxType[] = ['disbursement', 'transfer_out', 'fee_draw', 'refund', 'adjustment']

function direction(type: TrustTxType): 1 | -1 {
  return CREDIT_TYPES.includes(type) ? 1 : -1
}

@Injectable()
export class TrustService {
  constructor(
    @InjectModel(TrustAccount.name)     private accountModel: Model<TrustAccountDocument>,
    @InjectModel(TrustTransaction.name) private txModel:      Model<TrustTransactionDocument>,
  ) {}

  // Get or create a trust account for a client
  async getOrCreate(clientFolderId: string, currency = 'THB'): Promise<TrustAccountDocument> {
    let account = await this.accountModel.findOne({ clientFolderId }).exec()
    if (!account) {
      account = await this.accountModel.create({ clientFolderId, currency, bookBalance: 0, bankBalance: 0 })
    }
    return account
  }

  async addTransaction(dto: CreateTrustTransactionDto): Promise<TrustTransactionDocument> {
    const account = await this.getOrCreate(dto.clientFolderId, dto.currency)

    const delta = direction(dto.type as TrustTxType) * dto.amount
    if (delta < 0 && account.bookBalance + delta < -0.005) {
      throw new BadRequestException(`Insufficient trust balance. Current: ${account.bookBalance}, requested debit: ${dto.amount}`)
    }

    const tx = await this.txModel.create({
      ...dto,
      txDate: new Date(dto.txDate),
      currency: dto.currency ?? account.currency,
      reconciled: false,
    })

    // Update running book balance
    await this.accountModel.findByIdAndUpdate(account._id, { $inc: { bookBalance: delta } }).exec()

    return tx
  }

  async getTransactions(clientFolderId: string, limit = 100, offset = 0) {
    const [items, total] = await Promise.all([
      this.txModel.find({ clientFolderId }).sort({ txDate: -1 }).skip(offset).limit(limit).exec(),
      this.txModel.countDocuments({ clientFolderId }),
    ])
    return { items, total }
  }

  async getAccount(clientFolderId: string): Promise<TrustAccountDocument | null> {
    return this.accountModel.findOne({ clientFolderId }).exec()
  }

  // 3-way reconciliation: set bank balance and flag reconciled
  async reconcile(clientFolderId: string, dto: ReconcileDto): Promise<TrustAccountDocument> {
    const account = await this.getOrCreate(clientFolderId)
    const diff = Math.abs(account.bookBalance - dto.bankBalance)
    const isReconciled = diff < 0.005  // < 0.5 satang rounding tolerance

    const updated = await this.accountModel.findByIdAndUpdate(
      account._id,
      {
        bankBalance: dto.bankBalance,
        isReconciled,
        lastReconciledDate: new Date(),
        lastReconciledBy: dto.reconciledBy,
        ...(dto.notes ? { notes: dto.notes } : {}),
      },
      { new: true },
    ).exec()

    return updated!
  }

  // Firm-level: all trust accounts with balances and reconciliation status
  async getAllAccounts(limit = 200, offset = 0) {
    const [items, total] = await Promise.all([
      this.accountModel.find().sort({ updatedAt: -1 }).skip(offset).limit(limit).exec(),
      this.accountModel.countDocuments(),
    ])
    const totalTrustHeld = items.reduce((s, a) => s + a.bookBalance, 0)
    const unreconciled   = items.filter(a => !a.isReconciled).length
    return { items, total, totalTrustHeld, unreconciled }
  }
}
