/**
 * Quote Service
 * Mecánicas Diesel - ERP Suite
 *
 * Business logic for quotations management.
 */

import { Repository, DataSource } from 'typeorm';
import { Quote, QuoteStatus } from '../entities/quote.entity';
import { ServiceOrder, ServiceOrderStatus } from '../entities/service-order.entity';

// DTOs
export interface CreateQuoteDto {
  customerId: string;
  vehicleId: string;
  diagnosticId?: string;
  validityDays?: number;
  terms?: string;
  notes?: string;
}

export interface QuoteItemDto {
  itemType: 'service' | 'part';
  description: string;
  quantity: number;
  unitPrice: number;
  discountPct?: number;
  serviceId?: string;
  partId?: string;
}

export interface ApplyDiscountDto {
  discountPercent?: number;
  discountAmount?: number;
  discountReason?: string;
}

export class QuoteService {
  private quoteRepository: Repository<Quote>;
  private orderRepository: Repository<ServiceOrder>;

  constructor(private dataSource: DataSource) {
    this.quoteRepository = dataSource.getRepository(Quote);
    this.orderRepository = dataSource.getRepository(ServiceOrder);
  }

  /**
   * Generate next quote number for tenant
   */
  private async generateQuoteNumber(tenantId: string): Promise<string> {
    const year = new Date().getFullYear();
    const prefix = `COT-${year}-`;

    const lastQuote = await this.quoteRepository.findOne({
      where: { tenantId },
      order: { createdAt: 'DESC' },
    });

    let sequence = 1;
    if (lastQuote?.quoteNumber?.startsWith(prefix)) {
      const lastSeq = parseInt(lastQuote.quoteNumber.replace(prefix, ''), 10);
      sequence = isNaN(lastSeq) ? 1 : lastSeq + 1;
    }

    return `${prefix}${sequence.toString().padStart(5, '0')}`;
  }

  /**
   * Create a new quote
   */
  async create(tenantId: string, dto: CreateQuoteDto, userId?: string): Promise<Quote> {
    const quoteNumber = await this.generateQuoteNumber(tenantId);
    const validityDays = dto.validityDays || 15;

    const expiresAt = new Date();
    expiresAt.setDate(expiresAt.getDate() + validityDays);

    const quote = this.quoteRepository.create({
      tenantId,
      quoteNumber,
      customerId: dto.customerId,
      vehicleId: dto.vehicleId,
      diagnosticId: dto.diagnosticId,
      status: QuoteStatus.DRAFT,
      validityDays,
      expiresAt,
      terms: dto.terms,
      notes: dto.notes,
      createdBy: userId,
    });

    return this.quoteRepository.save(quote);
  }

  /**
   * Find quote by ID
   */
  async findById(tenantId: string, id: string): Promise<Quote | null> {
    return this.quoteRepository.findOne({
      where: { id, tenantId },
    });
  }

  /**
   * Find quote by number
   */
  async findByNumber(tenantId: string, quoteNumber: string): Promise<Quote | null> {
    return this.quoteRepository.findOne({
      where: { tenantId, quoteNumber },
    });
  }

  /**
   * List quotes with filters
   */
  async findAll(
    tenantId: string,
    filters: {
      status?: QuoteStatus;
      customerId?: string;
      vehicleId?: string;
      fromDate?: Date;
      toDate?: Date;
    } = {},
    pagination = { page: 1, limit: 20 }
  ) {
    const queryBuilder = this.quoteRepository.createQueryBuilder('quote')
      .where('quote.tenant_id = :tenantId', { tenantId });

    if (filters.status) {
      queryBuilder.andWhere('quote.status = :status', { status: filters.status });
    }
    if (filters.customerId) {
      queryBuilder.andWhere('quote.customer_id = :customerId', { customerId: filters.customerId });
    }
    if (filters.vehicleId) {
      queryBuilder.andWhere('quote.vehicle_id = :vehicleId', { vehicleId: filters.vehicleId });
    }
    if (filters.fromDate) {
      queryBuilder.andWhere('quote.created_at >= :fromDate', { fromDate: filters.fromDate });
    }
    if (filters.toDate) {
      queryBuilder.andWhere('quote.created_at <= :toDate', { toDate: filters.toDate });
    }

    const skip = (pagination.page - 1) * pagination.limit;

    const [data, total] = await queryBuilder
      .orderBy('quote.created_at', 'DESC')
      .skip(skip)
      .take(pagination.limit)
      .getManyAndCount();

    return {
      data,
      total,
      page: pagination.page,
      limit: pagination.limit,
      totalPages: Math.ceil(total / pagination.limit),
    };
  }

  /**
   * Send quote to customer
   */
  async send(tenantId: string, id: string, channel: 'email' | 'whatsapp'): Promise<Quote | null> {
    const quote = await this.findById(tenantId, id);
    if (!quote) return null;

    if (quote.status !== QuoteStatus.DRAFT) {
      throw new Error('Quote has already been sent');
    }

    quote.status = QuoteStatus.SENT;
    quote.sentAt = new Date();

    // TODO: Integrate with notification service
    // await notificationService.sendQuote(quote, channel);

    return this.quoteRepository.save(quote);
  }

  /**
   * Mark quote as viewed
   */
  async markViewed(tenantId: string, id: string): Promise<Quote | null> {
    const quote = await this.findById(tenantId, id);
    if (!quote) return null;

    if (!quote.viewedAt) {
      quote.viewedAt = new Date();
      if (quote.status === QuoteStatus.SENT) {
        quote.status = QuoteStatus.VIEWED;
      }
      return this.quoteRepository.save(quote);
    }

    return quote;
  }

  /**
   * Approve quote (customer action)
   */
  async approve(
    tenantId: string,
    id: string,
    approvalData: {
      approvedByName: string;
      approvalSignature?: string;
      approvalIp?: string;
    }
  ): Promise<Quote | null> {
    const quote = await this.findById(tenantId, id);
    if (!quote) return null;

    if (quote.status === QuoteStatus.EXPIRED) {
      throw new Error('Quote has expired');
    }
    if (quote.status === QuoteStatus.REJECTED) {
      throw new Error('Quote was rejected');
    }
    if (quote.status === QuoteStatus.APPROVED || quote.status === QuoteStatus.CONVERTED) {
      throw new Error('Quote has already been approved');
    }

    quote.status = QuoteStatus.APPROVED;
    quote.respondedAt = new Date();
    quote.approvedByName = approvalData.approvedByName;
    quote.approvalSignature = approvalData.approvalSignature;
    quote.approvalIp = approvalData.approvalIp;

    return this.quoteRepository.save(quote);
  }

  /**
   * Reject quote
   */
  async reject(tenantId: string, id: string, reason?: string): Promise<Quote | null> {
    const quote = await this.findById(tenantId, id);
    if (!quote) return null;

    quote.status = QuoteStatus.REJECTED;
    quote.respondedAt = new Date();
    if (reason) {
      quote.notes = `${quote.notes || ''}\n\nRejection reason: ${reason}`.trim();
    }

    return this.quoteRepository.save(quote);
  }

  /**
   * Convert quote to service order
   */
  async convertToOrder(tenantId: string, id: string, userId?: string): Promise<ServiceOrder | null> {
    const quote = await this.findById(tenantId, id);
    if (!quote) return null;

    if (quote.status !== QuoteStatus.APPROVED) {
      throw new Error('Quote must be approved before conversion');
    }

    // Generate order number
    const year = new Date().getFullYear();
    const prefix = `OS-${year}-`;
    const lastOrder = await this.orderRepository.findOne({
      where: { tenantId },
      order: { createdAt: 'DESC' },
    });

    let sequence = 1;
    if (lastOrder?.orderNumber?.startsWith(prefix)) {
      const lastSeq = parseInt(lastOrder.orderNumber.replace(prefix, ''), 10);
      sequence = isNaN(lastSeq) ? 1 : lastSeq + 1;
    }

    const orderNumber = `${prefix}${sequence.toString().padStart(5, '0')}`;

    // Create service order
    const order = this.orderRepository.create({
      tenantId,
      orderNumber,
      customerId: quote.customerId,
      vehicleId: quote.vehicleId,
      quoteId: quote.id,
      status: ServiceOrderStatus.APPROVED,
      laborTotal: quote.laborTotal,
      partsTotal: quote.partsTotal,
      discountAmount: quote.discountAmount,
      discountPercent: quote.discountPercent,
      tax: quote.tax,
      grandTotal: quote.grandTotal,
      customerNotes: quote.notes,
      createdBy: userId,
      receivedAt: new Date(),
    });

    const savedOrder = await this.orderRepository.save(order);

    // Update quote
    quote.status = QuoteStatus.CONVERTED;
    quote.convertedOrderId = savedOrder.id;
    await this.quoteRepository.save(quote);

    return savedOrder;
  }

  /**
   * Apply discount to quote
   */
  async applyDiscount(tenantId: string, id: string, dto: ApplyDiscountDto): Promise<Quote | null> {
    const quote = await this.findById(tenantId, id);
    if (!quote) return null;

    if (dto.discountPercent !== undefined) {
      quote.discountPercent = dto.discountPercent;
      const subtotal = Number(quote.laborTotal) + Number(quote.partsTotal);
      quote.discountAmount = subtotal * (dto.discountPercent / 100);
    } else if (dto.discountAmount !== undefined) {
      quote.discountAmount = dto.discountAmount;
      const subtotal = Number(quote.laborTotal) + Number(quote.partsTotal);
      quote.discountPercent = subtotal > 0 ? (dto.discountAmount / subtotal) * 100 : 0;
    }

    if (dto.discountReason) {
      quote.discountReason = dto.discountReason;
    }

    // Recalculate totals
    const subtotal = Number(quote.laborTotal) + Number(quote.partsTotal);
    const taxableAmount = subtotal - Number(quote.discountAmount);
    quote.tax = taxableAmount * 0.16; // 16% IVA
    quote.grandTotal = taxableAmount + quote.tax;

    return this.quoteRepository.save(quote);
  }

  /**
   * Check and update expired quotes
   */
  async updateExpiredQuotes(tenantId: string): Promise<number> {
    const result = await this.quoteRepository
      .createQueryBuilder()
      .update(Quote)
      .set({ status: QuoteStatus.EXPIRED })
      .where('tenant_id = :tenantId', { tenantId })
      .andWhere('status IN (:...statuses)', {
        statuses: [QuoteStatus.DRAFT, QuoteStatus.SENT, QuoteStatus.VIEWED],
      })
      .andWhere('expires_at < :now', { now: new Date() })
      .execute();

    return result.affected || 0;
  }

  /**
   * Get quote statistics
   */
  async getStats(tenantId: string): Promise<{
    total: number;
    pending: number;
    approved: number;
    rejected: number;
    converted: number;
    conversionRate: number;
    averageValue: number;
  }> {
    const [total, pending, approved, rejected, converted, valueResult] = await Promise.all([
      this.quoteRepository.count({ where: { tenantId } }),
      this.quoteRepository.count({
        where: { tenantId, status: QuoteStatus.SENT },
      }),
      this.quoteRepository.count({
        where: { tenantId, status: QuoteStatus.APPROVED },
      }),
      this.quoteRepository.count({
        where: { tenantId, status: QuoteStatus.REJECTED },
      }),
      this.quoteRepository.count({
        where: { tenantId, status: QuoteStatus.CONVERTED },
      }),
      this.quoteRepository
        .createQueryBuilder('quote')
        .select('AVG(quote.grand_total)', 'avg')
        .where('quote.tenant_id = :tenantId', { tenantId })
        .getRawOne(),
    ]);

    const totalResponded = approved + rejected + converted;
    const conversionRate = totalResponded > 0 ? ((approved + converted) / totalResponded) * 100 : 0;

    return {
      total,
      pending,
      approved,
      rejected,
      converted,
      conversionRate,
      averageValue: parseFloat(valueResult?.avg) || 0,
    };
  }
}
