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

export interface Timesheet {
  id: string;
  tenant_id: string;
  company_id: string;
  project_id: string;
  project_name?: string;
  task_id?: string;
  task_name?: string;
  user_id: string;
  user_name?: string;
  date: Date;
  hours: number;
  description?: string;
  billable: boolean;
  status: 'draft' | 'submitted' | 'approved' | 'rejected';
  created_at: Date;
}

export interface CreateTimesheetDto {
  company_id: string;
  project_id: string;
  task_id?: string;
  date: string;
  hours: number;
  description?: string;
  billable?: boolean;
}

export interface UpdateTimesheetDto {
  task_id?: string | null;
  date?: string;
  hours?: number;
  description?: string | null;
  billable?: boolean;
}

export interface TimesheetFilters {
  company_id?: string;
  project_id?: string;
  task_id?: string;
  user_id?: string;
  status?: string;
  date_from?: string;
  date_to?: string;
  page?: number;
  limit?: number;
}

class TimesheetsService {
  async findAll(tenantId: string, filters: TimesheetFilters = {}): Promise<{ data: Timesheet[]; total: number }> {
    const { company_id, project_id, task_id, user_id, status, date_from, date_to, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

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

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

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

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

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

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

    if (date_from) {
      whereClause += ` AND ts.date >= $${paramIndex++}`;
      params.push(date_from);
    }

    if (date_to) {
      whereClause += ` AND ts.date <= $${paramIndex++}`;
      params.push(date_to);
    }

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

    params.push(limit, offset);
    const data = await query<Timesheet>(
      `SELECT ts.*,
              p.name as project_name,
              t.name as task_name,
              u.name as user_name
       FROM projects.timesheets ts
       LEFT JOIN projects.projects p ON ts.project_id = p.id
       LEFT JOIN projects.tasks t ON ts.task_id = t.id
       LEFT JOIN auth.users u ON ts.user_id = u.id
       ${whereClause}
       ORDER BY ts.date DESC, ts.created_at DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findById(id: string, tenantId: string): Promise<Timesheet> {
    const timesheet = await queryOne<Timesheet>(
      `SELECT ts.*,
              p.name as project_name,
              t.name as task_name,
              u.name as user_name
       FROM projects.timesheets ts
       LEFT JOIN projects.projects p ON ts.project_id = p.id
       LEFT JOIN projects.tasks t ON ts.task_id = t.id
       LEFT JOIN auth.users u ON ts.user_id = u.id
       WHERE ts.id = $1 AND ts.tenant_id = $2`,
      [id, tenantId]
    );

    if (!timesheet) {
      throw new NotFoundError('Timesheet no encontrado');
    }

    return timesheet;
  }

  async create(dto: CreateTimesheetDto, tenantId: string, userId: string): Promise<Timesheet> {
    if (dto.hours <= 0 || dto.hours > 24) {
      throw new ValidationError('Las horas deben estar entre 0 y 24');
    }

    const timesheet = await queryOne<Timesheet>(
      `INSERT INTO projects.timesheets (
        tenant_id, company_id, project_id, task_id, user_id, date,
        hours, description, billable, created_by
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
       RETURNING *`,
      [
        tenantId, dto.company_id, dto.project_id, dto.task_id, userId,
        dto.date, dto.hours, dto.description, dto.billable ?? true, userId
      ]
    );

    return timesheet!;
  }

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

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

    if (existing.user_id !== userId) {
      throw new ValidationError('Solo puedes editar tus propios timesheets');
    }

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

    if (dto.task_id !== undefined) {
      updateFields.push(`task_id = $${paramIndex++}`);
      values.push(dto.task_id);
    }
    if (dto.date !== undefined) {
      updateFields.push(`date = $${paramIndex++}`);
      values.push(dto.date);
    }
    if (dto.hours !== undefined) {
      if (dto.hours <= 0 || dto.hours > 24) {
        throw new ValidationError('Las horas deben estar entre 0 y 24');
      }
      updateFields.push(`hours = $${paramIndex++}`);
      values.push(dto.hours);
    }
    if (dto.description !== undefined) {
      updateFields.push(`description = $${paramIndex++}`);
      values.push(dto.description);
    }
    if (dto.billable !== undefined) {
      updateFields.push(`billable = $${paramIndex++}`);
      values.push(dto.billable);
    }

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

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

    values.push(id, tenantId);

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

    return this.findById(id, tenantId);
  }

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

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

    if (existing.user_id !== userId) {
      throw new ValidationError('Solo puedes eliminar tus propios timesheets');
    }

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

  async submit(id: string, tenantId: string, userId: string): Promise<Timesheet> {
    const timesheet = await this.findById(id, tenantId);

    if (timesheet.status !== 'draft') {
      throw new ValidationError('Solo se pueden enviar timesheets en estado borrador');
    }

    if (timesheet.user_id !== userId) {
      throw new ValidationError('Solo puedes enviar tus propios timesheets');
    }

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

    return this.findById(id, tenantId);
  }

  async approve(id: string, tenantId: string, userId: string): Promise<Timesheet> {
    const timesheet = await this.findById(id, tenantId);

    if (timesheet.status !== 'submitted') {
      throw new ValidationError('Solo se pueden aprobar timesheets enviados');
    }

    await query(
      `UPDATE projects.timesheets SET status = 'approved', approved_by = $1, approved_at = CURRENT_TIMESTAMP,
       updated_by = $1, updated_at = CURRENT_TIMESTAMP
       WHERE id = $2 AND tenant_id = $3`,
      [userId, id, tenantId]
    );

    return this.findById(id, tenantId);
  }

  async reject(id: string, tenantId: string, userId: string): Promise<Timesheet> {
    const timesheet = await this.findById(id, tenantId);

    if (timesheet.status !== 'submitted') {
      throw new ValidationError('Solo se pueden rechazar timesheets enviados');
    }

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

    return this.findById(id, tenantId);
  }

  async getMyTimesheets(tenantId: string, userId: string, filters: TimesheetFilters = {}): Promise<{ data: Timesheet[]; total: number }> {
    return this.findAll(tenantId, { ...filters, user_id: userId });
  }

  async getPendingApprovals(tenantId: string, filters: TimesheetFilters = {}): Promise<{ data: Timesheet[]; total: number }> {
    return this.findAll(tenantId, { ...filters, status: 'submitted' });
  }
}

export const timesheetsService = new TimesheetsService();
