import { pool } from '../db/connection.js';
import logger from '../utils/logger.js';
import { notFound, conflict } from '../utils/errors.js';

// ============================================================================
// CREATE Task (append to end of project)
// ============================================================================

export const createTask = async (projectId, data, createdById) => {
  const client = await pool.connect();
  
  try {
    await client.query('BEGIN');
    
    // Verify project exists
    const projectCheck = await client.query(
      'SELECT id FROM projects WHERE id = $1',
      [projectId]
    );
    if (projectCheck.rows.length === 0) {
      throw { status: 404, code: 'RESOURCE_NOT_FOUND', message: 'Project not found' };
    }
    
    // Get max position (or 0 if no tasks)
    const maxPosResult = await client.query(
      'SELECT COALESCE(MAX(position), -1) + 1 as next_position FROM tasks WHERE project_id = $1',
      [projectId]
    );
    const nextPosition = maxPosResult.rows[0].next_position;
    
    // Insert task
    const query = `
      INSERT INTO tasks (
        project_id, title, description, status, position, due_date,
        assignee_id, created_by, created_at, updated_at
      )
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
      RETURNING id, project_id, title, description, status, position,
                due_date, assignee_id, created_by, created_at, updated_at;
    `;
    
    const result = await client.query(query, [
      projectId,
      data.title,
      data.description || null,
      data.status,
      nextPosition,
      data.due_date || null,
      data.assignee_id || null,
      createdById
    ]);
    
    await client.query('COMMIT');
    return result.rows[0];
  } catch (error) {
    await client.query('ROLLBACK');
    
    if (error.status) throw error;
    
    logger.error({
      message: 'Error creating task',
      error: error.message,
      projectId,
      data
    });
    throw error;
  } finally {
    client.release();
  }
};

// ============================================================================
// LIST Tasks with Filtering and Sorting
// ============================================================================

export const listTasks = async (projectId, filter) => {
  // Verify project exists
  const projectCheck = await pool.query(
    'SELECT id FROM projects WHERE id = $1',
    [projectId]
  );
  if (projectCheck.rows.length === 0) {
    notFound('Project');
  }
  
  let statusFilter = '';
  if (filter.status !== 'all') {
    statusFilter = `AND t.status = '${filter.status}'`;
  }
  
  let orderBy = 'position ASC';
  if (filter.sort === 'due_date') {
    orderBy = 'COALESCE(due_date, \'9999-12-31\') ASC, position ASC';
  } else if (filter.sort === 'created_at') {
    orderBy = 'created_at DESC';
  } else if (filter.sort === '-updated_at') {
    orderBy = 'updated_at DESC';
  }
  
  const query = `
    SELECT 
      id, project_id, title, description, status, position,
      due_date, assignee_id, created_by, created_at, updated_at
    FROM tasks
    WHERE project_id = $1 ${statusFilter}
    ORDER BY ${orderBy}
    LIMIT $2
    OFFSET $3;
  `;
  
  const countQuery = `
    SELECT COUNT(*)::INT as total
    FROM tasks
    WHERE project_id = $1 ${statusFilter};
  `;
  
  try {
    const [dataResult, countResult] = await Promise.all([
      pool.query(query, [projectId, filter.limit, filter.offset]),
      pool.query(countQuery, [projectId])
    ]);
    
    return {
      tasks: dataResult.rows,
      total: countResult.rows[0].total
    };
  } catch (error) {
    logger.error({
      message: 'Error listing tasks',
      error: error.message,
      projectId,
      filter
    });
    throw error;
  }
};

// ============================================================================
// GET Task Detail
// ============================================================================

export const getTask = async (projectId, taskId) => {
  const query = `
    SELECT 
      id, project_id, title, description, status, position,
      due_date, assignee_id, created_by, created_at, updated_at
    FROM tasks
    WHERE id = $1 AND project_id = $2;
  `;
  
  try {
    const result = await pool.query(query, [taskId, projectId]);
    
    if (result.rows.length === 0) {
      notFound('Task');
    }
    
    return result.rows[0];
  } catch (error) {
    if (error.status) throw error;
    
    logger.error({
      message: 'Error fetching task',
      error: error.message,
      projectId,
      taskId
    });
    throw error;
  }
};

// ============================================================================
// UPDATE Task with Position Reordering
// ============================================================================

export const updateTask = async (projectId, taskId, data) => {
  const client = await pool.connect();
  
  try {
    await client.query('BEGIN');
    
    // Verify project and task exist
    const taskCheck = await client.query(
      'SELECT id, position FROM tasks WHERE id = $1 AND project_id = $2',
      [taskId, projectId]
    );
    if (taskCheck.rows.length === 0) {
      throw { status: 404, code: 'RESOURCE_NOT_FOUND', message: 'Task not found' };
    }
    
    const currentTask = taskCheck.rows[0];
    const oldPosition = currentTask.position;
    
    // If position is provided, handle reordering
    let newPosition = oldPosition;
    if (data.position !== undefined) {
      // Get total task count
      const countResult = await client.query(
        'SELECT COUNT(*)::INT as total FROM tasks WHERE project_id = $1',
        [projectId]
      );
      const totalTasks = countResult.rows[0].total;
      
      // Validate new position
      if (data.position < 0 || data.position >= totalTasks) {
        throw {
          status: 409,
          code: 'CONFLICT',
          message: `Position must be between 0 and ${totalTasks - 1}`
        };
      }
      
      newPosition = data.position;
      
      // If position changed, perform reordering
      if (newPosition !== oldPosition) {
        // Get all task IDs sorted by position
        const allTasksResult = await client.query(
          'SELECT id FROM tasks WHERE project_id = $1 ORDER BY position ASC',
          [projectId]
        );
        const allTasks = allTasksResult.rows.map(r => r.id);
        
        // Remove task from old position
        allTasks.splice(oldPosition, 1);
        
        // Insert at new position
        allTasks.splice(newPosition, 0, taskId);
        
        // Batch update all positions
        for (let i = 0; i < allTasks.length; i++) {
          await client.query(
            'UPDATE tasks SET position = $1 WHERE id = $2',
            [i, allTasks[i]]
          );
        }
      }
    }
    
    // Build dynamic update query for other fields
    const updates = [];
    const values = [];
    let paramCount = 1;
    
    if (data.title !== undefined) {
      updates.push(`title = $${paramCount++}`);
      values.push(data.title);
    }
    
    if (data.description !== undefined) {
      updates.push(`description = $${paramCount++}`);
      values.push(data.description);
    }
    
    if (data.status !== undefined) {
      updates.push(`status = $${paramCount++}`);
      values.push(data.status);
    }
    
    if (data.due_date !== undefined) {
      updates.push(`due_date = $${paramCount++}`);
      values.push(data.due_date);
    }
    
    if (data.assignee_id !== undefined) {
      updates.push(`assignee_id = $${paramCount++}`);
      values.push(data.assignee_id);
    }
    
    // Always update updated_at
    updates.push(`updated_at = NOW()`);
    
    // Add task ID
    values.push(taskId);
    
    let query = `
      UPDATE tasks
      SET ${updates.join(', ')}
      WHERE id = $${paramCount}
      RETURNING id, project_id, title, description, status, position,
                due_date, assignee_id, created_by, created_at, updated_at;
    `;
    
    const result = await client.query(query, values);
    
    await client.query('COMMIT');
    return result.rows[0];
  } catch (error) {
    await client.query('ROLLBACK');
    
    if (error.status) throw error;
    
    logger.error({
      message: 'Error updating task',
      error: error.message,
      projectId,
      taskId,
      data
    });
    throw error;
  } finally {
    client.release();
  }
};

// ============================================================================
// DELETE Task (gaps OK, no renumbering)
// ============================================================================

export const deleteTask = async (projectId, taskId) => {
  const query = `
    DELETE FROM tasks
    WHERE id = $1 AND project_id = $2
    RETURNING id;
  `;
  
  try {
    const result = await pool.query(query, [taskId, projectId]);
    
    if (result.rows.length === 0) {
      notFound('Task');
    }
    
    return result.rows[0];
  } catch (error) {
    if (error.status) throw error;
    
    logger.error({
      message: 'Error deleting task',
      error: error.message,
      projectId,
      taskId
    });
    throw error;
  }
};

// ============================================================================
// BULK REORDER Tasks
// ============================================================================

export const reorderTasks = async (projectId, order) => {
  const client = await pool.connect();
  
  try {
    await client.query('BEGIN');
    
    // Verify all IDs belong to project
    const validationQuery = `
      SELECT COUNT(*)::INT as count FROM tasks
      WHERE project_id = $1 AND id = ANY($2::BIGINT[])
    `;
    const validationResult = await client.query(validationQuery, [
      projectId,
      order
    ]);
    
    if (validationResult.rows[0].count !== order.length) {
      throw {
        status: 422,
        code: 'UNPROCESSABLE_ENTITY',
        message: 'One or more task IDs do not belong to this project'
      };
    }
    
    // Update positions
    let updateCount = 0;
    for (let i = 0; i < order.length; i++) {
      const result = await client.query(
        'UPDATE tasks SET position = $1 WHERE id = $2 AND project_id = $3',
        [i, order[i], projectId]
      );
      updateCount += result.rowCount;
    }
    
    await client.query('COMMIT');
    return updateCount;
  } catch (error) {
    await client.query('ROLLBACK');
    
    if (error.status) throw error;
    
    logger.error({
      message: 'Error reordering tasks',
      error: error.message,
      projectId,
      order
    });
    throw error;
  } finally {
    client.release();
  }
};
