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

export interface Warehouse {
  id: string;
  tenant_id: string;
  company_id: string;
  company_name?: string;
  name: string;
  code: string;
  address_id?: string;
  is_default: boolean;
  active: boolean;
  created_at: Date;
  created_by?: string;
}

export interface Location {
  id: string;
  tenant_id: string;
  warehouse_id: string;
  warehouse_name?: string;
  name: string;
  code: string;
  location_type: 'internal' | 'supplier' | 'customer' | 'inventory' | 'production' | 'transit';
  parent_id?: string;
  is_scrap: boolean;
  is_return: boolean;
  active: boolean;
}

export interface CreateWarehouseDto {
  company_id: string;
  name: string;
  code: string;
  address_id?: string;
  is_default?: boolean;
}

export interface UpdateWarehouseDto {
  name?: string;
  address_id?: string | null;
  is_default?: boolean;
  active?: boolean;
}

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

class WarehousesService {
  async findAll(tenantId: string, filters: WarehouseFilters = {}): Promise<{ data: Warehouse[]; total: number }> {
    const { company_id, active, page = 1, limit = 50 } = filters;
    const offset = (page - 1) * limit;

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

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

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

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

    params.push(limit, offset);
    const data = await query<Warehouse>(
      `SELECT w.*, c.name as company_name
       FROM inventory.warehouses w
       LEFT JOIN auth.companies c ON w.company_id = c.id
       ${whereClause}
       ORDER BY w.name ASC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findById(id: string, tenantId: string): Promise<Warehouse> {
    const warehouse = await queryOne<Warehouse>(
      `SELECT w.*, c.name as company_name
       FROM inventory.warehouses w
       LEFT JOIN auth.companies c ON w.company_id = c.id
       WHERE w.id = $1 AND w.tenant_id = $2`,
      [id, tenantId]
    );

    if (!warehouse) {
      throw new NotFoundError('Almacén no encontrado');
    }

    return warehouse;
  }

  async create(dto: CreateWarehouseDto, tenantId: string, userId: string): Promise<Warehouse> {
    // Check unique code within company
    const existing = await queryOne<Warehouse>(
      `SELECT id FROM inventory.warehouses WHERE company_id = $1 AND code = $2`,
      [dto.company_id, dto.code]
    );
    if (existing) {
      throw new ConflictError(`Ya existe un almacén con código ${dto.code} en esta empresa`);
    }

    // If is_default, clear other defaults for company
    if (dto.is_default) {
      await query(
        `UPDATE inventory.warehouses SET is_default = false WHERE company_id = $1 AND tenant_id = $2`,
        [dto.company_id, tenantId]
      );
    }

    const warehouse = await queryOne<Warehouse>(
      `INSERT INTO inventory.warehouses (tenant_id, company_id, name, code, address_id, is_default, created_by)
       VALUES ($1, $2, $3, $4, $5, $6, $7)
       RETURNING *`,
      [tenantId, dto.company_id, dto.name, dto.code, dto.address_id, dto.is_default || false, userId]
    );

    return warehouse!;
  }

  async update(id: string, dto: UpdateWarehouseDto, tenantId: string, userId: string): Promise<Warehouse> {
    const existing = await this.findById(id, tenantId);

    // If setting as default, clear other defaults
    if (dto.is_default) {
      await query(
        `UPDATE inventory.warehouses SET is_default = false WHERE company_id = $1 AND tenant_id = $2 AND id != $3`,
        [existing.company_id, tenantId, id]
      );
    }

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

    if (dto.name !== undefined) {
      updateFields.push(`name = $${paramIndex++}`);
      values.push(dto.name);
    }
    if (dto.address_id !== undefined) {
      updateFields.push(`address_id = $${paramIndex++}`);
      values.push(dto.address_id);
    }
    if (dto.is_default !== undefined) {
      updateFields.push(`is_default = $${paramIndex++}`);
      values.push(dto.is_default);
    }
    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);

    const warehouse = await queryOne<Warehouse>(
      `UPDATE inventory.warehouses SET ${updateFields.join(', ')}
       WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex}
       RETURNING *`,
      values
    );

    return warehouse!;
  }

  async delete(id: string, tenantId: string): Promise<void> {
    await this.findById(id, tenantId);

    // Check if warehouse has locations with stock
    const hasStock = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count FROM inventory.stock_quants sq
       INNER JOIN inventory.locations l ON sq.location_id = l.id
       WHERE l.warehouse_id = $1 AND sq.quantity > 0`,
      [id]
    );

    if (parseInt(hasStock?.count || '0', 10) > 0) {
      throw new ConflictError('No se puede eliminar un almacén que tiene stock');
    }

    await query(`DELETE FROM inventory.warehouses WHERE id = $1 AND tenant_id = $2`, [id, tenantId]);
  }

  // Locations
  async getLocations(warehouseId: string, tenantId: string): Promise<Location[]> {
    await this.findById(warehouseId, tenantId);

    return query<Location>(
      `SELECT l.*, w.name as warehouse_name
       FROM inventory.locations l
       INNER JOIN inventory.warehouses w ON l.warehouse_id = w.id
       WHERE l.warehouse_id = $1 AND l.tenant_id = $2
       ORDER BY l.name`,
      [warehouseId, tenantId]
    );
  }

  async getStock(warehouseId: string, tenantId: string): Promise<any[]> {
    await this.findById(warehouseId, tenantId);

    return query(
      `SELECT sq.*, p.name as product_name, p.code as product_code, l.name as location_name
       FROM inventory.stock_quants sq
       INNER JOIN inventory.products p ON sq.product_id = p.id
       INNER JOIN inventory.locations l ON sq.location_id = l.id
       WHERE l.warehouse_id = $1
       ORDER BY p.name, l.name`,
      [warehouseId]
    );
  }
}

export const warehousesService = new WarehousesService();
