import { Injectable, Inject, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import { Settlement, SettlementStatus } from '../../domain/entities/settlement.entity';
import { JournalType } from '../../domain/entities/journal-entry.entity';
import { RefundStatus } from '../../domain/entities/refund.entity';
import {
  SETTLEMENT_REPOSITORY,
  ISettlementRepository,
} from '../../domain/repositories/settlement.repository.interface';
import {
  JOURNAL_ENTRY_REPOSITORY,
  IJournalEntryRepository,
} from '../../domain/repositories/journal-entry.repository.interface';
import {
  REFUND_REPOSITORY,
  IRefundRepository,
} from '../../domain/repositories/refund.repository.interface';

export interface FinanceSummary {
  totalFeesCollected: string;
  pendingSettlements: number;
  pendingSettlementAmount: string;
  completedSettlements: number;
  completedSettlementAmount: string;
  totalRefunds: number;
  totalRefundAmount: string;
  poolBalance: string;
}

export interface RevenueTrendItem {
  month: string;
  revenue: string;
  settlements: number;
  refunds: number;
}

@Injectable()
export class AdminFinanceService {
  private readonly logger = new Logger('AdminFinanceService');

  constructor(
    @Inject(SETTLEMENT_REPOSITORY)
    private readonly settlementRepo: ISettlementRepository,
    @Inject(JOURNAL_ENTRY_REPOSITORY)
    private readonly journalRepo: IJournalEntryRepository,
    @Inject(REFUND_REPOSITORY)
    private readonly refundRepo: IRefundRepository,
  ) {}

  /**
   * Aggregate platform finance overview from settlements + journal entries.
   */
  async getSummary(): Promise<FinanceSummary> {
    const [totalFeesCollected, pendingStats, completedStats, refundStats, completedRefundTotal] =
      await Promise.all([
        this.journalRepo.getSumByType(JournalType.TRADE_FEE),
        this.settlementRepo.getStatsByStatus(SettlementStatus.PENDING),
        this.settlementRepo.getStatsByStatus(SettlementStatus.COMPLETED),
        this.refundRepo.getRefundStats(),
        this.refundRepo.getCompletedRefundTotal(),
      ]);

    const poolBalance =
      parseFloat(completedStats.total) - parseFloat(completedRefundTotal);

    return {
      totalFeesCollected,
      pendingSettlements: pendingStats.count,
      pendingSettlementAmount: pendingStats.total,
      completedSettlements: completedStats.count,
      completedSettlementAmount: completedStats.total,
      totalRefunds: refundStats.count,
      totalRefundAmount: refundStats.total,
      poolBalance: String(poolBalance),
    };
  }

  /**
   * Paginated list of settlements with optional status filter.
   */
  async getSettlements(page: number, limit: number, status?: string) {
    const [items, total] = await this.settlementRepo.findAndCount({
      status: status as SettlementStatus | undefined,
      page,
      limit,
    });
    return { items, total, page, limit, totalPages: Math.ceil(total / limit) };
  }

  /**
   * Monthly revenue trend aggregated from journal entries (TRADE_FEE type).
   * Returns the last 12 months of data.
   */
  async getRevenueTrend(): Promise<RevenueTrendItem[]> {
    const [feeResults, settlementCounts, refundCounts] = await Promise.all([
      this.journalRepo.getMonthlyRevenueByType(JournalType.TRADE_FEE),
      this.settlementRepo.getMonthlySettlementCounts(),
      this.refundRepo.getMonthlyRefundCounts(),
    ]);

    // Merge results by month
    const monthMap = new Map<string, RevenueTrendItem>();

    for (const row of feeResults) {
      monthMap.set(row.month, {
        month: row.month,
        revenue: row.revenue,
        settlements: 0,
        refunds: 0,
      });
    }

    for (const row of settlementCounts) {
      const existing = monthMap.get(row.month);
      if (existing) {
        existing.settlements = row.settlements;
      } else {
        monthMap.set(row.month, {
          month: row.month,
          revenue: '0',
          settlements: row.settlements,
          refunds: 0,
        });
      }
    }

    for (const row of refundCounts) {
      const existing = monthMap.get(row.month);
      if (existing) {
        existing.refunds = row.refunds;
      } else {
        monthMap.set(row.month, {
          month: row.month,
          revenue: '0',
          settlements: 0,
          refunds: row.refunds,
        });
      }
    }

    return Array.from(monthMap.values()).sort((a, b) =>
      a.month.localeCompare(b.month),
    );
  }

  /**
   * Process a pending settlement: move status to PROCESSING then COMPLETED.
   */
  async processSettlement(id: string): Promise<Settlement> {
    const settlement = await this.settlementRepo.findById(id);
    if (!settlement) {
      throw new NotFoundException(`Settlement ${id} not found`);
    }
    if (
      settlement.status !== SettlementStatus.PENDING &&
      settlement.status !== SettlementStatus.PROCESSING
    ) {
      throw new BadRequestException(
        `Settlement ${id} cannot be processed (current status: ${settlement.status})`,
      );
    }

    if (settlement.status === SettlementStatus.PENDING) {
      settlement.status = SettlementStatus.PROCESSING;
      await this.settlementRepo.save(settlement);
    }

    // Mark as completed
    settlement.status = SettlementStatus.COMPLETED;
    settlement.settledAt = new Date();
    const saved = await this.settlementRepo.save(settlement);

    this.logger.log(`Settlement ${id} processed successfully`);
    return saved;
  }

  /**
   * Cancel a pending settlement.
   */
  async cancelSettlement(id: string): Promise<Settlement> {
    const settlement = await this.settlementRepo.findById(id);
    if (!settlement) {
      throw new NotFoundException(`Settlement ${id} not found`);
    }
    if (settlement.status !== SettlementStatus.PENDING) {
      throw new BadRequestException(
        `Only pending settlements can be cancelled (current status: ${settlement.status})`,
      );
    }

    settlement.status = SettlementStatus.FAILED;
    const saved = await this.settlementRepo.save(settlement);

    this.logger.log(`Settlement ${id} cancelled`);
    return saved;
  }

  /**
   * List consumer refund records with optional status filter.
   */
  async getConsumerRefunds(page: number, limit: number, status?: string) {
    const [items, total] = await this.refundRepo.findAndCount({
      status: status as RefundStatus | undefined,
      page,
      limit,
    });
    return { items, total, page, limit, totalPages: Math.ceil(total / limit) };
  }
}
