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

export interface Project {
  id: string;
  tenant_id: string;
  company_id: string;
  company_name?: string;
  name: string;
  code?: string;
  description?: string;
  manager_id?: string;
  manager_name?: string;
  partner_id?: string;
  partner_name?: string;
  analytic_account_id?: string;
  date_start?: Date;
  date_end?: Date;
  status: 'draft' | 'active' | 'completed' | 'cancelled' | 'on_hold';
  privacy: 'public' | 'private' | 'followers';
  allow_timesheets: boolean;
  color?: string;
  task_count?: number;
  completed_task_count?: number;
  created_at: Date;
}

export interface CreateProjectDto {
  company_id: string;
  name: string;
  code?: string;
  description?: string;
  manager_id?: string;
  partner_id?: string;
  date_start?: string;
  date_end?: string;
  privacy?: 'public' | 'private' | 'followers';
  allow_timesheets?: boolean;
  color?: string;
}

export interface UpdateProjectDto {
  name?: string;
  code?: string | null;
  description?: string | null;
  manager_id?: string | null;
  partner_id?: string | null;
  date_start?: string | null;
  date_end?: string | null;
  status?: 'draft' | 'active' | 'completed' | 'cancelled' | 'on_hold';
  privacy?: 'public' | 'private' | 'followers';
  allow_timesheets?: boolean;
  color?: string | null;
}

export interface ProjectFilters {
  company_id?: string;
  manager_id?: string;
  partner_id?: string;
  status?: string;
  search?: string;
  page?: number;
  limit?: number;
}

class ProjectsService {
  async findAll(tenantId: string, filters: ProjectFilters = {}): Promise<{ data: Project[]; total: number }> {
    const { company_id, manager_id, partner_id, status, search, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

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

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

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

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

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

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

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

    params.push(limit, offset);
    const data = await query<Project>(
      `SELECT p.*,
              c.name as company_name,
              u.name as manager_name,
              pr.name as partner_name,
              (SELECT COUNT(*) FROM projects.tasks t WHERE t.project_id = p.id AND t.deleted_at IS NULL) as task_count,
              (SELECT COUNT(*) FROM projects.tasks t WHERE t.project_id = p.id AND t.status = 'done' AND t.deleted_at IS NULL) as completed_task_count
       FROM projects.projects p
       LEFT JOIN auth.companies c ON p.company_id = c.id
       LEFT JOIN auth.users u ON p.manager_id = u.id
       LEFT JOIN core.partners pr ON p.partner_id = pr.id
       ${whereClause}
       ORDER BY p.created_at DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findById(id: string, tenantId: string): Promise<Project> {
    const project = await queryOne<Project>(
      `SELECT p.*,
              c.name as company_name,
              u.name as manager_name,
              pr.name as partner_name,
              (SELECT COUNT(*) FROM projects.tasks t WHERE t.project_id = p.id AND t.deleted_at IS NULL) as task_count,
              (SELECT COUNT(*) FROM projects.tasks t WHERE t.project_id = p.id AND t.status = 'done' AND t.deleted_at IS NULL) as completed_task_count
       FROM projects.projects p
       LEFT JOIN auth.companies c ON p.company_id = c.id
       LEFT JOIN auth.users u ON p.manager_id = u.id
       LEFT JOIN core.partners pr ON p.partner_id = pr.id
       WHERE p.id = $1 AND p.tenant_id = $2 AND p.deleted_at IS NULL`,
      [id, tenantId]
    );

    if (!project) {
      throw new NotFoundError('Proyecto no encontrado');
    }

    return project;
  }

  async create(dto: CreateProjectDto, tenantId: string, userId: string): Promise<Project> {
    // Check unique code if provided
    if (dto.code) {
      const existing = await queryOne(
        `SELECT id FROM projects.projects WHERE company_id = $1 AND code = $2 AND deleted_at IS NULL`,
        [dto.company_id, dto.code]
      );

      if (existing) {
        throw new ConflictError('Ya existe un proyecto con ese código');
      }
    }

    const project = await queryOne<Project>(
      `INSERT INTO projects.projects (
        tenant_id, company_id, name, code, description, manager_id, partner_id,
        date_start, date_end, privacy, allow_timesheets, color, created_by
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
       RETURNING *`,
      [
        tenantId, dto.company_id, dto.name, dto.code, dto.description,
        dto.manager_id, dto.partner_id, dto.date_start, dto.date_end,
        dto.privacy || 'public', dto.allow_timesheets ?? true, dto.color, userId
      ]
    );

    return project!;
  }

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

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

    if (dto.name !== undefined) {
      updateFields.push(`name = $${paramIndex++}`);
      values.push(dto.name);
    }
    if (dto.code !== undefined) {
      if (dto.code) {
        const existingCode = await queryOne(
          `SELECT id FROM projects.projects WHERE company_id = $1 AND code = $2 AND id != $3 AND deleted_at IS NULL`,
          [existing.company_id, dto.code, id]
        );
        if (existingCode) {
          throw new ConflictError('Ya existe un proyecto con ese código');
        }
      }
      updateFields.push(`code = $${paramIndex++}`);
      values.push(dto.code);
    }
    if (dto.description !== undefined) {
      updateFields.push(`description = $${paramIndex++}`);
      values.push(dto.description);
    }
    if (dto.manager_id !== undefined) {
      updateFields.push(`manager_id = $${paramIndex++}`);
      values.push(dto.manager_id);
    }
    if (dto.partner_id !== undefined) {
      updateFields.push(`partner_id = $${paramIndex++}`);
      values.push(dto.partner_id);
    }
    if (dto.date_start !== undefined) {
      updateFields.push(`date_start = $${paramIndex++}`);
      values.push(dto.date_start);
    }
    if (dto.date_end !== undefined) {
      updateFields.push(`date_end = $${paramIndex++}`);
      values.push(dto.date_end);
    }
    if (dto.status !== undefined) {
      updateFields.push(`status = $${paramIndex++}`);
      values.push(dto.status);
    }
    if (dto.privacy !== undefined) {
      updateFields.push(`privacy = $${paramIndex++}`);
      values.push(dto.privacy);
    }
    if (dto.allow_timesheets !== undefined) {
      updateFields.push(`allow_timesheets = $${paramIndex++}`);
      values.push(dto.allow_timesheets);
    }
    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.projects 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.projects SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
       WHERE id = $2 AND tenant_id = $3`,
      [userId, id, tenantId]
    );
  }

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

    const stats = await queryOne<{
      total_tasks: number;
      completed_tasks: number;
      in_progress_tasks: number;
      total_hours: number;
      total_milestones: number;
      completed_milestones: number;
    }>(
      `SELECT
         (SELECT COUNT(*) FROM projects.tasks WHERE project_id = $1 AND deleted_at IS NULL) as total_tasks,
         (SELECT COUNT(*) FROM projects.tasks WHERE project_id = $1 AND status = 'done' AND deleted_at IS NULL) as completed_tasks,
         (SELECT COUNT(*) FROM projects.tasks WHERE project_id = $1 AND status = 'in_progress' AND deleted_at IS NULL) as in_progress_tasks,
         (SELECT COALESCE(SUM(hours), 0) FROM projects.timesheets WHERE project_id = $1) as total_hours,
         (SELECT COUNT(*) FROM projects.milestones WHERE project_id = $1) as total_milestones,
         (SELECT COUNT(*) FROM projects.milestones WHERE project_id = $1 AND status = 'completed') as completed_milestones`,
      [id]
    );

    return {
      total_tasks: parseInt(String(stats?.total_tasks || 0)),
      completed_tasks: parseInt(String(stats?.completed_tasks || 0)),
      in_progress_tasks: parseInt(String(stats?.in_progress_tasks || 0)),
      completion_percentage: stats?.total_tasks
        ? Math.round((parseInt(String(stats.completed_tasks)) / parseInt(String(stats.total_tasks))) * 100)
        : 0,
      total_hours: parseFloat(String(stats?.total_hours || 0)),
      total_milestones: parseInt(String(stats?.total_milestones || 0)),
      completed_milestones: parseInt(String(stats?.completed_milestones || 0)),
    };
  }
}

export const projectsService = new ProjectsService();
