import { query, queryOne, getClient } from '../../config/database.js';
import { NotFoundError, ConflictError, ValidationError } from '../../shared/errors/index.js';

export interface PricelistItem {
  id: string;
  pricelist_id: string;
  product_id?: string;
  product_name?: string;
  product_category_id?: string;
  category_name?: string;
  price: number;
  min_quantity: number;
  valid_from?: Date;
  valid_to?: Date;
  active: boolean;
}

export interface Pricelist {
  id: string;
  tenant_id: string;
  company_id?: string;
  company_name?: string;
  name: string;
  currency_id: string;
  currency_code?: string;
  active: boolean;
  items?: PricelistItem[];
  created_at: Date;
}

export interface CreatePricelistDto {
  company_id?: string;
  name: string;
  currency_id: string;
}

export interface UpdatePricelistDto {
  name?: string;
  currency_id?: string;
  active?: boolean;
}

export interface CreatePricelistItemDto {
  product_id?: string;
  product_category_id?: string;
  price: number;
  min_quantity?: number;
  valid_from?: string;
  valid_to?: string;
}

export interface PricelistFilters {
  company_id?: string;
  active?: boolean;
  page?: number;
  limit?: number;
}

class PricelistsService {
  async findAll(tenantId: string, filters: PricelistFilters = {}): Promise<{ data: Pricelist[]; total: number }> {
    const { company_id, active, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = 'WHERE p.tenant_id = $1';
    const params: any[] = [tenantId];
    let paramIndex = 2;

    if (company_id) {
      whereClause += ` AND p.company_id = $${paramIndex++}`;
      params.push(company_id);
    }

    if (active !== undefined) {
      whereClause += ` AND p.active = $${paramIndex++}`;
      params.push(active);
    }

    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count FROM sales.pricelists p ${whereClause}`,
      params
    );

    params.push(limit, offset);
    const data = await query<Pricelist>(
      `SELECT p.*,
              c.name as company_name,
              cu.code as currency_code
       FROM sales.pricelists p
       LEFT JOIN auth.companies c ON p.company_id = c.id
       LEFT JOIN core.currencies cu ON p.currency_id = cu.id
       ${whereClause}
       ORDER BY p.name
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findById(id: string, tenantId: string): Promise<Pricelist> {
    const pricelist = await queryOne<Pricelist>(
      `SELECT p.*,
              c.name as company_name,
              cu.code as currency_code
       FROM sales.pricelists p
       LEFT JOIN auth.companies c ON p.company_id = c.id
       LEFT JOIN core.currencies cu ON p.currency_id = cu.id
       WHERE p.id = $1 AND p.tenant_id = $2`,
      [id, tenantId]
    );

    if (!pricelist) {
      throw new NotFoundError('Lista de precios no encontrada');
    }

    // Get items
    const items = await query<PricelistItem>(
      `SELECT pi.*,
              pr.name as product_name,
              pc.name as category_name
       FROM sales.pricelist_items pi
       LEFT JOIN inventory.products pr ON pi.product_id = pr.id
       LEFT JOIN core.product_categories pc ON pi.product_category_id = pc.id
       WHERE pi.pricelist_id = $1
       ORDER BY pi.min_quantity, pr.name`,
      [id]
    );

    pricelist.items = items;

    return pricelist;
  }

  async create(dto: CreatePricelistDto, tenantId: string, userId: string): Promise<Pricelist> {
    // Check unique name
    const existing = await queryOne(
      `SELECT id FROM sales.pricelists WHERE tenant_id = $1 AND name = $2`,
      [tenantId, dto.name]
    );

    if (existing) {
      throw new ConflictError('Ya existe una lista de precios con ese nombre');
    }

    const pricelist = await queryOne<Pricelist>(
      `INSERT INTO sales.pricelists (tenant_id, company_id, name, currency_id, created_by)
       VALUES ($1, $2, $3, $4, $5)
       RETURNING *`,
      [tenantId, dto.company_id, dto.name, dto.currency_id, userId]
    );

    return pricelist!;
  }

  async update(id: string, dto: UpdatePricelistDto, tenantId: string, userId: string): Promise<Pricelist> {
    await this.findById(id, tenantId);

    const updateFields: string[] = [];
    const values: any[] = [];
    let paramIndex = 1;

    if (dto.name !== undefined) {
      // Check unique name
      const existing = await queryOne(
        `SELECT id FROM sales.pricelists WHERE tenant_id = $1 AND name = $2 AND id != $3`,
        [tenantId, dto.name, id]
      );
      if (existing) {
        throw new ConflictError('Ya existe una lista de precios con ese nombre');
      }
      updateFields.push(`name = $${paramIndex++}`);
      values.push(dto.name);
    }
    if (dto.currency_id !== undefined) {
      updateFields.push(`currency_id = $${paramIndex++}`);
      values.push(dto.currency_id);
    }
    if (dto.active !== undefined) {
      updateFields.push(`active = $${paramIndex++}`);
      values.push(dto.active);
    }

    updateFields.push(`updated_by = $${paramIndex++}`);
    values.push(userId);
    updateFields.push(`updated_at = CURRENT_TIMESTAMP`);

    values.push(id, tenantId);

    await query(
      `UPDATE sales.pricelists SET ${updateFields.join(', ')}
       WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex}`,
      values
    );

    return this.findById(id, tenantId);
  }

  async addItem(pricelistId: string, dto: CreatePricelistItemDto, tenantId: string, userId: string): Promise<PricelistItem> {
    await this.findById(pricelistId, tenantId);

    if (!dto.product_id && !dto.product_category_id) {
      throw new ValidationError('Debe especificar un producto o una categoría');
    }

    if (dto.product_id && dto.product_category_id) {
      throw new ValidationError('Debe especificar solo un producto o solo una categoría, no ambos');
    }

    const item = await queryOne<PricelistItem>(
      `INSERT INTO sales.pricelist_items (pricelist_id, product_id, product_category_id, price, min_quantity, valid_from, valid_to, created_by)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
       RETURNING *`,
      [pricelistId, dto.product_id, dto.product_category_id, dto.price, dto.min_quantity || 1, dto.valid_from, dto.valid_to, userId]
    );

    return item!;
  }

  async removeItem(pricelistId: string, itemId: string, tenantId: string): Promise<void> {
    await this.findById(pricelistId, tenantId);

    const result = await query(
      `DELETE FROM sales.pricelist_items WHERE id = $1 AND pricelist_id = $2`,
      [itemId, pricelistId]
    );
  }

  async getProductPrice(productId: string, pricelistId: string, quantity: number = 1): Promise<number | null> {
    const item = await queryOne<{ price: number }>(
      `SELECT price FROM sales.pricelist_items
       WHERE pricelist_id = $1
         AND (product_id = $2 OR product_category_id = (SELECT category_id FROM inventory.products WHERE id = $2))
         AND active = true
         AND min_quantity <= $3
         AND (valid_from IS NULL OR valid_from <= CURRENT_DATE)
         AND (valid_to IS NULL OR valid_to >= CURRENT_DATE)
       ORDER BY product_id NULLS LAST, min_quantity DESC
       LIMIT 1`,
      [pricelistId, productId, quantity]
    );

    return item?.price || null;
  }
}

export const pricelistsService = new PricelistsService();
