import { Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { Invoice, InvoiceStatus } from '../../domain/entities/invoice.entity';
import { InvoiceItem } from '../../domain/entities/invoice-item.entity';

@Injectable()
export class InvoiceRepository {
  private readonly repo: Repository<Invoice>;
  private readonly itemRepo: Repository<InvoiceItem>;

  constructor(private readonly dataSource: DataSource) {
    this.repo = this.dataSource.getRepository(Invoice);
    this.itemRepo = this.dataSource.getRepository(InvoiceItem);
  }

  async findByTenantId(tenantId: string, limit = 20, offset = 0): Promise<[Invoice[], number]> {
    return this.repo.findAndCount({
      where: { tenantId },
      order: { createdAt: 'DESC' },
      take: limit,
      skip: offset,
    });
  }

  async findById(id: string): Promise<Invoice | null> {
    return this.repo.findOne({ where: { id }, relations: ['items'] });
  }

  async findByInvoiceNumber(invoiceNumber: string): Promise<Invoice | null> {
    return this.repo.findOne({ where: { invoiceNumber } });
  }

  async findUnpaidByTenantId(tenantId: string): Promise<Invoice[]> {
    return this.repo.find({
      where: [
        { tenantId, status: InvoiceStatus.OPEN },
        { tenantId, status: InvoiceStatus.PAST_DUE },
      ],
    });
  }

  async getNextInvoiceNumber(): Promise<string> {
    const result = await this.dataSource.query(
      `SELECT nextval('billing_invoice_number_seq') AS seq`,
    );
    const seq = result[0]?.seq ?? Date.now();
    const year = new Date().getFullYear();
    return `INV-${year}-${String(seq).padStart(6, '0')}`;
  }

  async save(invoice: Invoice): Promise<Invoice> {
    return this.repo.save(invoice);
  }

  async saveItems(items: InvoiceItem[]): Promise<InvoiceItem[]> {
    return this.itemRepo.save(items);
  }

  async saveWithItems(invoice: Invoice, items: InvoiceItem[]): Promise<Invoice> {
    const runner = this.dataSource.createQueryRunner();
    await runner.connect();
    await runner.startTransaction();
    try {
      const savedInvoice = await runner.manager.save(Invoice, invoice);
      for (const item of items) {
        item.invoiceId = savedInvoice.id;
      }
      await runner.manager.save(InvoiceItem, items);
      await runner.commitTransaction();
      return savedInvoice;
    } catch (err) {
      await runner.rollbackTransaction();
      throw err;
    } finally {
      await runner.release();
    }
  }
}
