import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { TenantConnectionService } from '../../database/tenant-connection.service';
import { requireCurrentTenant } from '../../database/tenant-context';
import { SavingsAccount } from '../../database/tenant/entities/savings-account.entity';
import { SavingsTransaction } from '../../database/tenant/entities/savings-transaction.entity';

export interface CreateSavingsAccountDto {
  memberId?: string;
  officeId?: string;
  accountType?: string;
  openDate?: string;
  openDateBs?: string;
  interestRate?: number;
  minimumBalance?: number;
  fiscalYear?: string;
  remarks?: string;
}

export interface UpdateSavingsAccountDto extends Partial<CreateSavingsAccountDto> {
  status?: string;
  closeDate?: string;
  closeDateBs?: string;
}

export interface TransactionDto {
  type: 'deposit' | 'withdrawal' | 'interest' | 'penalty';
  amount: number;
  transactionDate?: string;
  transactionDateBs?: string;
  description?: string;
  voucherNumber?: string;
}

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

  private sub() {
    return requireCurrentTenant().subdomain;
  }

  private async ds() {
    return this.tenantConn.getTenantDataSource(this.sub());
  }

  private async repo() {
    return (await this.ds()).getRepository(SavingsAccount);
  }

  private async txRepo() {
    return (await this.ds()).getRepository(SavingsTransaction);
  }

  private async nextAccountNumber(): Promise<string> {
    const r = await this.repo();
    const count = await r.count({ withDeleted: true });
    return `SAV-${String(count + 1).padStart(5, '0')}`;
  }

  async findAll(opts: { q?: string; status?: string; accountType?: string; page?: number; limit?: number }) {
    const r = await this.repo();
    const { q, status, accountType, page = 1, limit = 50 } = opts;
    const qb = r
      .createQueryBuilder('a')
      .select([
        'a.id', 'a.accountNumber', 'a.memberId', 'a.officeId',
        'a.accountType', 'a.currentBalance', 'a.interestRate',
        'a.openDateBs', 'a.status', 'a.createdAt',
      ])
      .leftJoin('a.member', 'member')
      .addSelect(['member.id', 'member.name', 'member.nameEn', 'member.memberId', 'member.photoUrl'])
      .leftJoin('a.office', 'office')
      .addSelect(['office.id', 'office.name'])
      .where('a.deletedAt IS NULL')
      .orderBy('a.createdAt', 'DESC')
      .skip((page - 1) * limit)
      .take(limit);

    if (status) qb.andWhere('a.status = :status', { status });
    if (accountType) qb.andWhere('a.accountType = :accountType', { accountType });
    if (q) {
      qb.andWhere(
        '(a.accountNumber LIKE :q OR member.name LIKE :q OR member.nameEn LIKE :q OR member.memberId LIKE :q)',
        { q: `%${q}%` },
      );
    }

    const [items, total] = await qb.getManyAndCount();
    const totals = await r
      .createQueryBuilder('a')
      .select('SUM(a.currentBalance)', 'totalBalance')
      .addSelect('COUNT(*)', 'totalAccounts')
      .where('a.deletedAt IS NULL AND a.status = :s', { s: 'active' })
      .getRawOne();

    return {
      items,
      total,
      page,
      limit,
      summary: {
        totalBalance: Number(totals?.totalBalance ?? 0),
        totalAccounts: Number(totals?.totalAccounts ?? 0),
      },
    };
  }

  async findOne(id: string) {
    const r = await this.repo();
    const a = await r.findOne({
      where: { id },
      relations: ['member', 'office'],
    });
    if (!a) throw new NotFoundException('Savings account not found');
    return a;
  }

  async findTransactions(accountId: string, opts: { page?: number; limit?: number }) {
    await this.findOne(accountId);
    const txr = await this.txRepo();
    const { page = 1, limit = 50 } = opts;
    const [items, total] = await txr.findAndCount({
      where: { accountId },
      order: { createdAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
    });
    return { items, total, page, limit };
  }

  async create(dto: CreateSavingsAccountDto, userId?: string) {
    const r = await this.repo();
    const accountNumber = await this.nextAccountNumber();
    const account = r.create({
      ...dto,
      accountNumber,
      currentBalance: 0,
      status: 'active',
    });
    return r.save(account);
  }

  async update(id: string, dto: UpdateSavingsAccountDto) {
    const r = await this.repo();
    const a = await this.findOne(id);
    Object.assign(a, dto);
    return r.save(a);
  }

  async transact(accountId: string, dto: TransactionDto, userId?: string) {
    const ds = await this.ds();
    const accRepo = ds.getRepository(SavingsAccount);
    const txRepo = ds.getRepository(SavingsTransaction);

    const account = await accRepo.findOne({ where: { id: accountId } });
    if (!account) throw new NotFoundException('Account not found');
    if (account.status !== 'active') throw new BadRequestException('Account is not active');

    const current = Number(account.currentBalance);
    const amount = Number(dto.amount);

    if (dto.type === 'withdrawal' || dto.type === 'penalty') {
      if (amount > current) throw new BadRequestException('अपर्याप्त मौज्दात (Insufficient balance)');
    }

    const isCredit = dto.type === 'deposit' || dto.type === 'interest';
    const newBalance = isCredit ? current + amount : current - amount;

    const tx = txRepo.create({
      accountId,
      type: dto.type,
      amount,
      balanceAfter: newBalance,
      transactionDate: dto.transactionDate,
      transactionDateBs: dto.transactionDateBs,
      description: dto.description,
      voucherNumber: dto.voucherNumber,
      createdBy: userId,
    });

    account.currentBalance = newBalance;

    await txRepo.save(tx);
    await accRepo.save(account);
    return tx;
  }

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