import { FindOptionsWhere, IsNull } from 'typeorm';
import { AppDataSource } from '../../../config/typeorm.js';
import { Invoice, Payment, InvoiceStatus } from '../entities/index.js';

export interface InvoiceSearchParams {
  tenantId: string;
  invoiceType?: string;
  partnerId?: string;
  status?: string;
  limit?: number;
  offset?: number;
}

export interface CreateInvoiceDto {
  invoiceType: 'sale' | 'purchase' | 'credit_note' | 'debit_note';
  partnerId: string;
  invoiceDate?: string;
  dueDate?: string;
  currency?: string;
  subtotal?: number;
  taxAmount?: number;
  totalAmount?: number;
  notes?: string;
}

export interface UpdateInvoiceDto {
  partnerId?: string;
  invoiceDate?: string;
  dueDate?: string;
  currency?: string;
  subtotal?: number;
  taxAmount?: number;
  totalAmount?: number;
  notes?: string;
  status?: InvoiceStatus;
}

export interface CreatePaymentDto {
  partnerId: string;
  paymentType: 'received' | 'made';
  paymentMethod: string;
  amount: number;
  currency?: string;
  paymentDate?: string;
  reference?: string;
  notes?: string;
  partnerName?: string;
}

export class InvoicesService {
  private get invoiceRepository() {
    return AppDataSource.getRepository(Invoice);
  }

  private get paymentRepository() {
    return AppDataSource.getRepository(Payment);
  }

  async findAllInvoices(params: InvoiceSearchParams): Promise<{ data: Invoice[]; total: number }> {
    const { tenantId, invoiceType, partnerId, status, limit = 50, offset = 0 } = params;
    const where: FindOptionsWhere<Invoice> = { tenantId, deletedAt: IsNull() };
    if (invoiceType) where.invoiceType = invoiceType as any;
    if (partnerId) where.partnerId = partnerId;
    if (status) where.status = status as any;
    const [data, total] = await this.invoiceRepository.findAndCount({
      where,
      take: limit,
      skip: offset,
      order: { createdAt: 'DESC' },
      relations: ['items'],
    });
    return { data, total };
  }

  async findInvoice(id: string, tenantId: string): Promise<Invoice | null> {
    return this.invoiceRepository.findOne({
      where: { id, tenantId, deletedAt: IsNull() },
      relations: ['items'],
    });
  }

  async createInvoice(tenantId: string, dto: CreateInvoiceDto, createdBy?: string): Promise<Invoice> {
    const count = await this.invoiceRepository.count({ where: { tenantId, invoiceType: dto.invoiceType } });
    const prefix = dto.invoiceType === 'sale' ? 'FAC' : dto.invoiceType === 'purchase' ? 'FP' : dto.invoiceType === 'credit_note' ? 'NC' : 'ND';
    const invoiceNumber = `${prefix}-${String(count + 1).padStart(6, '0')}`;
    const invoice = this.invoiceRepository.create({
      ...dto,
      tenantId,
      invoiceNumber,
      createdBy,
      invoiceDate: dto.invoiceDate ? new Date(dto.invoiceDate) : new Date(),
      dueDate: dto.dueDate ? new Date(dto.dueDate) : undefined,
      status: 'draft',
    });
    return this.invoiceRepository.save(invoice);
  }

  async updateInvoice(id: string, tenantId: string, dto: UpdateInvoiceDto, updatedBy?: string): Promise<Invoice | null> {
    const invoice = await this.findInvoice(id, tenantId);
    if (!invoice) return null;
    Object.assign(invoice, { ...dto, updatedBy });
    return this.invoiceRepository.save(invoice);
  }

  async deleteInvoice(id: string, tenantId: string): Promise<boolean> {
    const result = await this.invoiceRepository.softDelete({ id, tenantId });
    return (result.affected ?? 0) > 0;
  }

  async validateInvoice(id: string, tenantId: string, userId?: string): Promise<Invoice | null> {
    const invoice = await this.findInvoice(id, tenantId);
    if (!invoice || invoice.status !== 'draft') return null;
    invoice.status = 'validated';
    invoice.updatedBy = userId ?? null;
    return this.invoiceRepository.save(invoice);
  }

  async cancelInvoice(id: string, tenantId: string, userId?: string): Promise<Invoice | null> {
    const invoice = await this.findInvoice(id, tenantId);
    if (!invoice || invoice.status === 'cancelled') return null;
    invoice.status = 'cancelled';
    invoice.updatedBy = userId ?? null;
    return this.invoiceRepository.save(invoice);
  }

  async findAllPayments(tenantId: string, partnerId?: string, limit = 50, offset = 0): Promise<{ data: Payment[]; total: number }> {
    const where: FindOptionsWhere<Payment> = { tenantId, deletedAt: IsNull() };
    if (partnerId) where.partnerId = partnerId;
    const [data, total] = await this.paymentRepository.findAndCount({
      where,
      take: limit,
      skip: offset,
      order: { createdAt: 'DESC' },
    });
    return { data, total };
  }

  async findPayment(id: string, tenantId: string): Promise<Payment | null> {
    return this.paymentRepository.findOne({ where: { id, tenantId, deletedAt: IsNull() } });
  }

  async createPayment(tenantId: string, dto: CreatePaymentDto, createdBy?: string): Promise<Payment> {
    const count = await this.paymentRepository.count({ where: { tenantId } });
    const paymentNumber = `PAG-${String(count + 1).padStart(6, '0')}`;
    const payment = this.paymentRepository.create({
      ...dto,
      tenantId,
      paymentNumber,
      createdBy,
      paymentDate: dto.paymentDate ? new Date(dto.paymentDate) : new Date(),
      status: 'draft',
    });
    return this.paymentRepository.save(payment);
  }

  async confirmPayment(id: string, tenantId: string, _userId?: string): Promise<Payment | null> {
    const payment = await this.findPayment(id, tenantId);
    if (!payment || payment.status !== 'draft') return null;
    payment.status = 'confirmed';
    return this.paymentRepository.save(payment);
  }

  async cancelPayment(id: string, tenantId: string, _userId?: string): Promise<Payment | null> {
    const payment = await this.findPayment(id, tenantId);
    if (!payment || payment.status === 'cancelled') return null;
    payment.status = 'cancelled';
    return this.paymentRepository.save(payment);
  }
}

// ===== Export Singleton Instance =====
export const invoicesService = new InvoicesService();
