import {
  Injectable,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between, LessThanOrEqual, MoreThanOrEqual } from 'typeorm';
import { Sale, SaleStatus } from './entities/sale.entity';
import { SaleItem } from './entities/sale-item.entity';
import { Product } from '../products/entities/product.entity';
import { Tenant } from '../auth/entities/tenant.entity';
import { CreateSaleDto, CancelSaleDto, SalesFilterDto } from './dto/sale.dto';

interface TodaySummary {
  totalSales: number;
  totalRevenue: number;
  totalTax: number;
  avgTicket: number;
}

@Injectable()
export class SalesService {
  constructor(
    @InjectRepository(Sale)
    private readonly saleRepository: Repository<Sale>,
    @InjectRepository(SaleItem)
    private readonly saleItemRepository: Repository<SaleItem>,
    @InjectRepository(Product)
    private readonly productRepository: Repository<Product>,
    @InjectRepository(Tenant)
    private readonly tenantRepository: Repository<Tenant>,
  ) {}

  async findAll(tenantId: string, filters: SalesFilterDto): Promise<Sale[]> {
    const query = this.saleRepository
      .createQueryBuilder('sale')
      .leftJoinAndSelect('sale.items', 'items')
      .leftJoinAndSelect('sale.paymentMethod', 'paymentMethod')
      .where('sale.tenantId = :tenantId', { tenantId });

    if (filters.startDate && filters.endDate) {
      query.andWhere('DATE(sale.createdAt) BETWEEN :startDate AND :endDate', {
        startDate: filters.startDate,
        endDate: filters.endDate,
      });
    } else if (filters.startDate) {
      query.andWhere('DATE(sale.createdAt) >= :startDate', {
        startDate: filters.startDate,
      });
    } else if (filters.endDate) {
      query.andWhere('DATE(sale.createdAt) <= :endDate', {
        endDate: filters.endDate,
      });
    }

    if (filters.status) {
      query.andWhere('sale.status = :status', { status: filters.status });
    }

    if (filters.ticketNumber) {
      query.andWhere('sale.ticketNumber ILIKE :ticketNumber', {
        ticketNumber: `%${filters.ticketNumber}%`,
      });
    }

    query.orderBy('sale.createdAt', 'DESC');

    if (filters.limit) {
      query.limit(filters.limit);
    }

    return query.getMany();
  }

  async findOne(tenantId: string, id: string): Promise<Sale> {
    const sale = await this.saleRepository.findOne({
      where: { id, tenantId },
      relations: ['items', 'paymentMethod'],
    });

    if (!sale) {
      throw new NotFoundException('Venta no encontrada');
    }

    return sale;
  }

  async findByTicketNumber(tenantId: string, ticketNumber: string): Promise<Sale> {
    const sale = await this.saleRepository.findOne({
      where: { ticketNumber, tenantId },
      relations: ['items', 'paymentMethod'],
    });

    if (!sale) {
      throw new NotFoundException('Venta no encontrada');
    }

    return sale;
  }

  async create(tenantId: string, dto: CreateSaleDto): Promise<Sale> {
    // Check monthly sales limit
    const tenant = await this.tenantRepository.findOne({
      where: { id: tenantId },
    });

    if (!tenant) {
      throw new BadRequestException('Tenant no encontrado');
    }

    if (tenant.currentMonthSales >= tenant.maxSalesPerMonth) {
      throw new BadRequestException(
        `Has alcanzado el límite de ${tenant.maxSalesPerMonth} ventas este mes. Actualiza tu plan.`,
      );
    }

    // Calculate totals
    let subtotal = 0;
    const saleItems: Partial<SaleItem>[] = [];

    for (const item of dto.items) {
      const product = await this.productRepository.findOne({
        where: { id: item.productId, tenantId, isActive: true },
      });

      if (!product) {
        throw new BadRequestException(`Producto ${item.productId} no encontrado`);
      }

      // Check stock if tracking
      if (product.trackStock && Number(product.stockQuantity) < item.quantity) {
        throw new BadRequestException(
          `Stock insuficiente para ${product.name}. Disponible: ${product.stockQuantity}`,
        );
      }

      const itemSubtotal =
        Number(product.price) * item.quantity * (1 - (item.discountPercent || 0) / 100);

      saleItems.push({
        productId: product.id,
        productName: product.name,
        productSku: product.sku,
        quantity: item.quantity,
        unitPrice: Number(product.price),
        discountPercent: item.discountPercent || 0,
        subtotal: itemSubtotal,
      });

      subtotal += itemSubtotal;
    }

    // Calculate tax (assume 16% IVA included in price)
    const taxRate = Number(tenant.taxRate) / 100;
    const taxAmount = subtotal - subtotal / (1 + taxRate);
    const total = subtotal;

    // Validate payment
    if (dto.amountReceived < total) {
      throw new BadRequestException(
        `Monto recibido ($${dto.amountReceived}) es menor al total ($${total.toFixed(2)})`,
      );
    }

    const changeAmount = dto.amountReceived - total;

    // Create sale
    const sale = this.saleRepository.create({
      tenantId,
      subtotal,
      taxAmount,
      discountAmount: 0,
      total,
      paymentMethodId: dto.paymentMethodId,
      amountReceived: dto.amountReceived,
      changeAmount,
      customerName: dto.customerName,
      customerPhone: dto.customerPhone,
      notes: dto.notes,
      deviceInfo: dto.deviceInfo,
      status: SaleStatus.COMPLETED,
    });

    const savedSale = await this.saleRepository.save(sale);

    // Create sale items
    for (const item of saleItems) {
      const saleItem = this.saleItemRepository.create({
        ...item,
        saleId: savedSale.id,
      });
      await this.saleItemRepository.save(saleItem);
    }

    // Return complete sale with items
    return this.findOne(tenantId, savedSale.id);
  }

  async cancel(tenantId: string, id: string, dto: CancelSaleDto): Promise<Sale> {
    const sale = await this.findOne(tenantId, id);

    if (sale.status !== SaleStatus.COMPLETED) {
      throw new BadRequestException('Solo se pueden cancelar ventas completadas');
    }

    // Check if sale is from today (can only cancel same-day sales)
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const saleDate = new Date(sale.createdAt);
    saleDate.setHours(0, 0, 0, 0);

    if (saleDate.getTime() !== today.getTime()) {
      throw new BadRequestException('Solo se pueden cancelar ventas del día actual');
    }

    sale.status = SaleStatus.CANCELLED;
    sale.cancelledAt = new Date();
    sale.cancelReason = dto.reason;

    // Restore stock
    for (const item of sale.items) {
      if (item.productId) {
        const product = await this.productRepository.findOne({
          where: { id: item.productId },
        });

        if (product?.trackStock) {
          product.stockQuantity = Number(product.stockQuantity) + Number(item.quantity);
          await this.productRepository.save(product);
        }
      }
    }

    return this.saleRepository.save(sale);
  }

  async getTodaySummary(tenantId: string): Promise<TodaySummary> {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const result = await this.saleRepository
      .createQueryBuilder('sale')
      .select([
        'COUNT(sale.id) as totalSales',
        'COALESCE(SUM(sale.total), 0) as totalRevenue',
        'COALESCE(SUM(sale.taxAmount), 0) as totalTax',
        'COALESCE(AVG(sale.total), 0) as avgTicket',
      ])
      .where('sale.tenantId = :tenantId', { tenantId })
      .andWhere('DATE(sale.createdAt) = CURRENT_DATE')
      .andWhere('sale.status = :status', { status: SaleStatus.COMPLETED })
      .getRawOne();

    return {
      totalSales: parseInt(result.totalsales, 10) || 0,
      totalRevenue: parseFloat(result.totalrevenue) || 0,
      totalTax: parseFloat(result.totaltax) || 0,
      avgTicket: parseFloat(result.avgticket) || 0,
    };
  }

  async getRecentSales(tenantId: string, limit = 10): Promise<Sale[]> {
    return this.saleRepository.find({
      where: { tenantId },
      relations: ['items', 'paymentMethod'],
      order: { createdAt: 'DESC' },
      take: limit,
    });
  }
}
