import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import { Settlement, SettlementStatus } from '../../domain/entities/settlement.entity';
import { JournalEntry } from '../../domain/entities/journal-entry.entity';
import { ISettlementRepository } from '../../domain/repositories/settlement.repository.interface';

@Injectable()
export class SettlementRepository implements ISettlementRepository {
  constructor(
    @InjectRepository(Settlement)
    private readonly repo: Repository<Settlement>,
    private readonly dataSource: DataSource,
  ) {}

  async findById(id: string): Promise<Settlement | null> {
    return this.repo.findOne({ where: { id } });
  }

  async findByTradeId(tradeId: string): Promise<Settlement | null> {
    return this.repo.findOne({ where: { tradeId } });
  }

  async findAndCount(options: {
    status?: SettlementStatus;
    page: number;
    limit: number;
  }): Promise<[Settlement[], number]> {
    const qb = this.repo.createQueryBuilder('s');

    if (options.status) {
      qb.where('s.status = :status', { status: options.status });
    }

    qb.orderBy('s.created_at', 'DESC')
      .skip((options.page - 1) * options.limit)
      .take(options.limit);

    return qb.getManyAndCount();
  }

  async save(settlement: Settlement): Promise<Settlement> {
    return this.repo.save(settlement);
  }

  async createSettlementWithJournalEntries(
    settlementData: Partial<Settlement>,
    journalEntriesData: Partial<JournalEntry>[],
  ): Promise<Settlement> {
    return this.dataSource.transaction(async (manager) => {
      const settlement = manager.create(Settlement, settlementData);
      const saved = await manager.save(settlement);

      const entries = journalEntriesData.map((entry) =>
        manager.create(JournalEntry, entry),
      );
      // Set referenceId from the saved settlement
      for (const entry of entries) {
        if (!entry.referenceId) {
          entry.referenceId = saved.id;
        }
      }
      await manager.save(entries);

      return saved;
    });
  }

  async getStatsByStatus(
    status: SettlementStatus,
  ): Promise<{ count: number; total: string }> {
    const result = await this.repo
      .createQueryBuilder('s')
      .select('COUNT(s.id)', 'count')
      .addSelect('COALESCE(SUM(s.amount::numeric), 0)', 'total')
      .where('s.status = :status', { status })
      .getRawOne();

    return {
      count: parseInt(result?.count || '0', 10),
      total: result?.total || '0',
    };
  }

  async getMonthlySettlementCounts(): Promise<
    Array<{ month: string; settlements: number }>
  > {
    const results = await this.repo
      .createQueryBuilder('s')
      .select("TO_CHAR(s.created_at, 'YYYY-MM')", 'month')
      .addSelect('COUNT(s.id)', 'settlements')
      .where("s.created_at >= NOW() - INTERVAL '12 months'")
      .groupBy("TO_CHAR(s.created_at, 'YYYY-MM')")
      .getRawMany();

    return results.map((row) => ({
      month: row.month,
      settlements: parseInt(row.settlements, 10),
    }));
  }
}
