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

export interface Task {
  id: string;
  tenant_id: string;
  project_id: string;
  project_name?: string;
  stage_id?: string;
  stage_name?: string;
  name: string;
  description?: string;
  assigned_to?: string;
  assigned_name?: string;
  parent_id?: string;
  parent_name?: string;
  date_deadline?: Date;
  estimated_hours?: number;
  spent_hours?: number;
  priority: 'low' | 'normal' | 'high' | 'urgent';
  status: 'todo' | 'in_progress' | 'review' | 'done' | 'cancelled';
  sequence: number;
  color?: string;
  created_at: Date;
}

export interface CreateTaskDto {
  project_id: string;
  stage_id?: string;
  name: string;
  description?: string;
  assigned_to?: string;
  parent_id?: string;
  date_deadline?: string;
  estimated_hours?: number;
  priority?: 'low' | 'normal' | 'high' | 'urgent';
  color?: string;
}

export interface UpdateTaskDto {
  stage_id?: string | null;
  name?: string;
  description?: string | null;
  assigned_to?: string | null;
  parent_id?: string | null;
  date_deadline?: string | null;
  estimated_hours?: number | null;
  priority?: 'low' | 'normal' | 'high' | 'urgent';
  status?: 'todo' | 'in_progress' | 'review' | 'done' | 'cancelled';
  sequence?: number;
  color?: string | null;
}

export interface TaskFilters {
  project_id?: string;
  stage_id?: string;
  assigned_to?: string;
  status?: string;
  priority?: string;
  search?: string;
  page?: number;
  limit?: number;
}

class TasksService {
  async findAll(tenantId: string, filters: TaskFilters = {}): Promise<{ data: Task[]; total: number }> {
    const { project_id, stage_id, assigned_to, status, priority, search, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = 'WHERE t.tenant_id = $1 AND t.deleted_at IS NULL';
    const params: any[] = [tenantId];
    let paramIndex = 2;

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

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

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

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

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

    if (search) {
      whereClause += ` AND t.name ILIKE $${paramIndex}`;
      params.push(`%${search}%`);
      paramIndex++;
    }

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

    params.push(limit, offset);
    const data = await query<Task>(
      `SELECT t.*,
              p.name as project_name,
              ps.name as stage_name,
              u.name as assigned_name,
              pt.name as parent_name,
              COALESCE((SELECT SUM(hours) FROM projects.timesheets WHERE task_id = t.id), 0) as spent_hours
       FROM projects.tasks t
       LEFT JOIN projects.projects p ON t.project_id = p.id
       LEFT JOIN projects.project_stages ps ON t.stage_id = ps.id
       LEFT JOIN auth.users u ON t.assigned_to = u.id
       LEFT JOIN projects.tasks pt ON t.parent_id = pt.id
       ${whereClause}
       ORDER BY t.sequence, t.created_at DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findById(id: string, tenantId: string): Promise<Task> {
    const task = await queryOne<Task>(
      `SELECT t.*,
              p.name as project_name,
              ps.name as stage_name,
              u.name as assigned_name,
              pt.name as parent_name,
              COALESCE((SELECT SUM(hours) FROM projects.timesheets WHERE task_id = t.id), 0) as spent_hours
       FROM projects.tasks t
       LEFT JOIN projects.projects p ON t.project_id = p.id
       LEFT JOIN projects.project_stages ps ON t.stage_id = ps.id
       LEFT JOIN auth.users u ON t.assigned_to = u.id
       LEFT JOIN projects.tasks pt ON t.parent_id = pt.id
       WHERE t.id = $1 AND t.tenant_id = $2 AND t.deleted_at IS NULL`,
      [id, tenantId]
    );

    if (!task) {
      throw new NotFoundError('Tarea no encontrada');
    }

    return task;
  }

  async create(dto: CreateTaskDto, tenantId: string, userId: string): Promise<Task> {
    // Get next sequence for project
    const seqResult = await queryOne<{ max_seq: number }>(
      `SELECT COALESCE(MAX(sequence), 0) + 1 as max_seq FROM projects.tasks WHERE project_id = $1 AND deleted_at IS NULL`,
      [dto.project_id]
    );

    const task = await queryOne<Task>(
      `INSERT INTO projects.tasks (
        tenant_id, project_id, stage_id, name, description, assigned_to, parent_id,
        date_deadline, estimated_hours, priority, sequence, color, created_by
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
       RETURNING *`,
      [
        tenantId, dto.project_id, dto.stage_id, dto.name, dto.description,
        dto.assigned_to, dto.parent_id, dto.date_deadline, dto.estimated_hours,
        dto.priority || 'normal', seqResult?.max_seq || 1, dto.color, userId
      ]
    );

    return task!;
  }

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

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

    if (dto.stage_id !== undefined) {
      updateFields.push(`stage_id = $${paramIndex++}`);
      values.push(dto.stage_id);
    }
    if (dto.name !== undefined) {
      updateFields.push(`name = $${paramIndex++}`);
      values.push(dto.name);
    }
    if (dto.description !== undefined) {
      updateFields.push(`description = $${paramIndex++}`);
      values.push(dto.description);
    }
    if (dto.assigned_to !== undefined) {
      updateFields.push(`assigned_to = $${paramIndex++}`);
      values.push(dto.assigned_to);
    }
    if (dto.parent_id !== undefined) {
      if (dto.parent_id === id) {
        throw new ValidationError('Una tarea no puede ser su propio padre');
      }
      updateFields.push(`parent_id = $${paramIndex++}`);
      values.push(dto.parent_id);
    }
    if (dto.date_deadline !== undefined) {
      updateFields.push(`date_deadline = $${paramIndex++}`);
      values.push(dto.date_deadline);
    }
    if (dto.estimated_hours !== undefined) {
      updateFields.push(`estimated_hours = $${paramIndex++}`);
      values.push(dto.estimated_hours);
    }
    if (dto.priority !== undefined) {
      updateFields.push(`priority = $${paramIndex++}`);
      values.push(dto.priority);
    }
    if (dto.status !== undefined) {
      updateFields.push(`status = $${paramIndex++}`);
      values.push(dto.status);
    }
    if (dto.sequence !== undefined) {
      updateFields.push(`sequence = $${paramIndex++}`);
      values.push(dto.sequence);
    }
    if (dto.color !== undefined) {
      updateFields.push(`color = $${paramIndex++}`);
      values.push(dto.color);
    }

    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.tasks SET ${updateFields.join(', ')}
       WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex} AND deleted_at IS NULL`,
      values
    );

    return this.findById(id, tenantId);
  }

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

    // Soft delete
    await query(
      `UPDATE projects.tasks SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
       WHERE id = $2 AND tenant_id = $3`,
      [userId, id, tenantId]
    );
  }

  async move(id: string, stageId: string | null, sequence: number, tenantId: string, userId: string): Promise<Task> {
    const task = await this.findById(id, tenantId);

    await query(
      `UPDATE projects.tasks SET stage_id = $1, sequence = $2, updated_by = $3, updated_at = CURRENT_TIMESTAMP
       WHERE id = $4 AND tenant_id = $5`,
      [stageId, sequence, userId, id, tenantId]
    );

    return this.findById(id, tenantId);
  }

  async assign(id: string, userId: string, tenantId: string, currentUserId: string): Promise<Task> {
    await this.findById(id, tenantId);

    await query(
      `UPDATE projects.tasks SET assigned_to = $1, updated_by = $2, updated_at = CURRENT_TIMESTAMP
       WHERE id = $3 AND tenant_id = $4`,
      [userId, currentUserId, id, tenantId]
    );

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

export const tasksService = new TasksService();
