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

export type ContractStatus = 'draft' | 'active' | 'expired' | 'terminated' | 'cancelled';
export type ContractType = 'permanent' | 'temporary' | 'contractor' | 'internship' | 'part_time';

export interface Contract {
  id: string;
  tenant_id: string;
  company_id: string;
  company_name?: string;
  employee_id: string;
  employee_name?: string;
  employee_number?: string;
  name: string;
  reference?: string;
  contract_type: ContractType;
  status: ContractStatus;
  job_position_id?: string;
  job_position_name?: string;
  department_id?: string;
  department_name?: string;
  date_start: Date;
  date_end?: Date;
  trial_date_end?: Date;
  wage: number;
  wage_type: string;
  currency_id?: string;
  currency_code?: string;
  hours_per_week: number;
  vacation_days: number;
  christmas_bonus_days: number;
  document_url?: string;
  notes?: string;
  created_at: Date;
}

export interface CreateContractDto {
  company_id: string;
  employee_id: string;
  name: string;
  reference?: string;
  contract_type: ContractType;
  job_position_id?: string;
  department_id?: string;
  date_start: string;
  date_end?: string;
  trial_date_end?: string;
  wage: number;
  wage_type?: string;
  currency_id?: string;
  hours_per_week?: number;
  vacation_days?: number;
  christmas_bonus_days?: number;
  document_url?: string;
  notes?: string;
}

export interface UpdateContractDto {
  reference?: string | null;
  job_position_id?: string | null;
  department_id?: string | null;
  date_end?: string | null;
  trial_date_end?: string | null;
  wage?: number;
  wage_type?: string;
  currency_id?: string | null;
  hours_per_week?: number;
  vacation_days?: number;
  christmas_bonus_days?: number;
  document_url?: string | null;
  notes?: string | null;
}

export interface ContractFilters {
  company_id?: string;
  employee_id?: string;
  status?: ContractStatus;
  contract_type?: ContractType;
  search?: string;
  page?: number;
  limit?: number;
}

class ContractsService {
  async findAll(tenantId: string, filters: ContractFilters = {}): Promise<{ data: Contract[]; total: number }> {
    const { company_id, employee_id, status, contract_type, search, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

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

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

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

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

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

    if (search) {
      whereClause += ` AND (c.name ILIKE $${paramIndex} OR c.reference ILIKE $${paramIndex} OR e.first_name ILIKE $${paramIndex} OR e.last_name ILIKE $${paramIndex})`;
      params.push(`%${search}%`);
      paramIndex++;
    }

    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count
       FROM hr.contracts c
       LEFT JOIN hr.employees e ON c.employee_id = e.id
       ${whereClause}`,
      params
    );

    params.push(limit, offset);
    const data = await query<Contract>(
      `SELECT c.*,
              co.name as company_name,
              CONCAT(e.first_name, ' ', e.last_name) as employee_name,
              e.employee_number,
              j.name as job_position_name,
              d.name as department_name,
              cu.code as currency_code
       FROM hr.contracts c
       LEFT JOIN auth.companies co ON c.company_id = co.id
       LEFT JOIN hr.employees e ON c.employee_id = e.id
       LEFT JOIN hr.job_positions j ON c.job_position_id = j.id
       LEFT JOIN hr.departments d ON c.department_id = d.id
       LEFT JOIN core.currencies cu ON c.currency_id = cu.id
       ${whereClause}
       ORDER BY c.date_start DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findById(id: string, tenantId: string): Promise<Contract> {
    const contract = await queryOne<Contract>(
      `SELECT c.*,
              co.name as company_name,
              CONCAT(e.first_name, ' ', e.last_name) as employee_name,
              e.employee_number,
              j.name as job_position_name,
              d.name as department_name,
              cu.code as currency_code
       FROM hr.contracts c
       LEFT JOIN auth.companies co ON c.company_id = co.id
       LEFT JOIN hr.employees e ON c.employee_id = e.id
       LEFT JOIN hr.job_positions j ON c.job_position_id = j.id
       LEFT JOIN hr.departments d ON c.department_id = d.id
       LEFT JOIN core.currencies cu ON c.currency_id = cu.id
       WHERE c.id = $1 AND c.tenant_id = $2`,
      [id, tenantId]
    );

    if (!contract) {
      throw new NotFoundError('Contrato no encontrado');
    }

    return contract;
  }

  async create(dto: CreateContractDto, tenantId: string, userId: string): Promise<Contract> {
    // Check if employee has an active contract
    const activeContract = await queryOne(
      `SELECT id FROM hr.contracts WHERE employee_id = $1 AND status = 'active'`,
      [dto.employee_id]
    );

    if (activeContract) {
      throw new ValidationError('El empleado ya tiene un contrato activo');
    }

    const contract = await queryOne<Contract>(
      `INSERT INTO hr.contracts (
        tenant_id, company_id, employee_id, name, reference, contract_type,
        job_position_id, department_id, date_start, date_end, trial_date_end,
        wage, wage_type, currency_id, hours_per_week, vacation_days, christmas_bonus_days,
        document_url, notes, created_by
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
       RETURNING *`,
      [
        tenantId, dto.company_id, dto.employee_id, dto.name, dto.reference, dto.contract_type,
        dto.job_position_id, dto.department_id, dto.date_start, dto.date_end, dto.trial_date_end,
        dto.wage, dto.wage_type || 'monthly', dto.currency_id, dto.hours_per_week || 40,
        dto.vacation_days || 6, dto.christmas_bonus_days || 15, dto.document_url, dto.notes, userId
      ]
    );

    return this.findById(contract!.id, tenantId);
  }

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

    if (existing.status !== 'draft') {
      throw new ValidationError('Solo se pueden editar contratos en estado borrador');
    }

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

    const fieldsToUpdate = [
      'reference', 'job_position_id', 'department_id', 'date_end', 'trial_date_end',
      'wage', 'wage_type', 'currency_id', 'hours_per_week', 'vacation_days',
      'christmas_bonus_days', 'document_url', 'notes'
    ];

    for (const field of fieldsToUpdate) {
      if ((dto as any)[field] !== undefined) {
        updateFields.push(`${field} = $${paramIndex++}`);
        values.push((dto as any)[field]);
      }
    }

    if (updateFields.length === 0) {
      return existing;
    }

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

    values.push(id, tenantId);

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

    return this.findById(id, tenantId);
  }

  async activate(id: string, tenantId: string, userId: string): Promise<Contract> {
    const contract = await this.findById(id, tenantId);

    if (contract.status !== 'draft') {
      throw new ValidationError('Solo se pueden activar contratos en estado borrador');
    }

    // Check if employee has another active contract
    const activeContract = await queryOne(
      `SELECT id FROM hr.contracts WHERE employee_id = $1 AND status = 'active' AND id != $2`,
      [contract.employee_id, id]
    );

    if (activeContract) {
      throw new ValidationError('El empleado ya tiene otro contrato activo');
    }

    await query(
      `UPDATE hr.contracts SET
         status = 'active',
         updated_by = $1,
         updated_at = CURRENT_TIMESTAMP
       WHERE id = $2 AND tenant_id = $3`,
      [userId, id, tenantId]
    );

    // Update employee department and position if specified
    if (contract.department_id || contract.job_position_id) {
      await query(
        `UPDATE hr.employees SET
           department_id = COALESCE($1, department_id),
           job_position_id = COALESCE($2, job_position_id),
           updated_by = $3,
           updated_at = CURRENT_TIMESTAMP
         WHERE id = $4`,
        [contract.department_id, contract.job_position_id, userId, contract.employee_id]
      );
    }

    return this.findById(id, tenantId);
  }

  async terminate(id: string, terminationDate: string, tenantId: string, userId: string): Promise<Contract> {
    const contract = await this.findById(id, tenantId);

    if (contract.status !== 'active') {
      throw new ValidationError('Solo se pueden terminar contratos activos');
    }

    await query(
      `UPDATE hr.contracts SET
         status = 'terminated',
         date_end = $1,
         updated_by = $2,
         updated_at = CURRENT_TIMESTAMP
       WHERE id = $3 AND tenant_id = $4`,
      [terminationDate, userId, id, tenantId]
    );

    return this.findById(id, tenantId);
  }

  async cancel(id: string, tenantId: string, userId: string): Promise<Contract> {
    const contract = await this.findById(id, tenantId);

    if (contract.status === 'active' || contract.status === 'terminated') {
      throw new ValidationError('No se puede cancelar un contrato activo o terminado');
    }

    await query(
      `UPDATE hr.contracts SET
         status = 'cancelled',
         updated_by = $1,
         updated_at = CURRENT_TIMESTAMP
       WHERE id = $2 AND tenant_id = $3`,
      [userId, id, tenantId]
    );

    return this.findById(id, tenantId);
  }

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

    if (contract.status !== 'draft' && contract.status !== 'cancelled') {
      throw new ValidationError('Solo se pueden eliminar contratos en borrador o cancelados');
    }

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

export const contractsService = new ContractsService();
