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

export interface Activity {
  id: string;
  tenant_id: string;
  model: string;
  record_id: string;
  activity_type: 'call' | 'meeting' | 'email' | 'todo' | 'follow_up' | 'custom';
  summary: string;
  description?: string;
  assigned_to?: string;
  assigned_to_name?: string;
  assigned_by?: string;
  assigned_by_name?: string;
  due_date: Date;
  due_time?: string;
  status: 'planned' | 'done' | 'cancelled' | 'overdue';
  created_at: Date;
  created_by?: string;
  completed_at?: Date;
  completed_by?: string;
}

export interface CreateActivityDto {
  model: string;
  record_id: string;
  activity_type: 'call' | 'meeting' | 'email' | 'todo' | 'follow_up' | 'custom';
  summary: string;
  description?: string;
  assigned_to?: string;
  due_date: string;
  due_time?: string;
}

export interface UpdateActivityDto {
  activity_type?: 'call' | 'meeting' | 'email' | 'todo' | 'follow_up' | 'custom';
  summary?: string;
  description?: string | null;
  assigned_to?: string | null;
  due_date?: string;
  due_time?: string | null;
}

export interface ActivityFilters {
  model?: string;
  record_id?: string;
  activity_type?: string;
  assigned_to?: string;
  status?: string;
  due_from?: string;
  due_to?: string;
  overdue_only?: boolean;
  search?: string;
  page?: number;
  limit?: number;
}

class ActivitiesService {
  async findAll(tenantId: string, filters: ActivityFilters = {}): Promise<{ data: Activity[]; total: number }> {
    const { model, record_id, activity_type, assigned_to, status, due_from, due_to, overdue_only, search, page = 1, limit = 50 } = filters;
    const offset = (page - 1) * limit;

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

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

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

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

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

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

    if (due_from) {
      whereClause += ` AND a.due_date >= $${paramIndex++}`;
      params.push(due_from);
    }

    if (due_to) {
      whereClause += ` AND a.due_date <= $${paramIndex++}`;
      params.push(due_to);
    }

    if (overdue_only) {
      whereClause += ` AND a.status = 'planned' AND a.due_date < CURRENT_DATE`;
    }

    if (search) {
      whereClause += ` AND (a.summary ILIKE $${paramIndex} OR a.description ILIKE $${paramIndex})`;
      params.push(`%${search}%`);
      paramIndex++;
    }

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

    params.push(limit, offset);
    const data = await query<Activity>(
      `SELECT a.*,
              uto.first_name || ' ' || uto.last_name as assigned_to_name,
              uby.first_name || ' ' || uby.last_name as assigned_by_name
       FROM system.activities a
       LEFT JOIN auth.users uto ON a.assigned_to = uto.id
       LEFT JOIN auth.users uby ON a.assigned_by = uby.id
       ${whereClause}
       ORDER BY a.due_date ASC, a.due_time ASC NULLS LAST, a.created_at DESC
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

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

  async findByRecord(model: string, recordId: string, tenantId: string): Promise<Activity[]> {
    const activities = await query<Activity>(
      `SELECT a.*,
              uto.first_name || ' ' || uto.last_name as assigned_to_name,
              uby.first_name || ' ' || uby.last_name as assigned_by_name
       FROM system.activities a
       LEFT JOIN auth.users uto ON a.assigned_to = uto.id
       LEFT JOIN auth.users uby ON a.assigned_by = uby.id
       WHERE a.model = $1 AND a.record_id = $2 AND a.tenant_id = $3
       ORDER BY a.due_date ASC, a.due_time ASC NULLS LAST`,
      [model, recordId, tenantId]
    );

    return activities;
  }

  async findByUser(userId: string, tenantId: string, status?: string): Promise<Activity[]> {
    let whereClause = 'WHERE a.assigned_to = $1 AND a.tenant_id = $2';
    const params: any[] = [userId, tenantId];

    if (status) {
      whereClause += ' AND a.status = $3';
      params.push(status);
    }

    const activities = await query<Activity>(
      `SELECT a.*,
              uto.first_name || ' ' || uto.last_name as assigned_to_name,
              uby.first_name || ' ' || uby.last_name as assigned_by_name
       FROM system.activities a
       LEFT JOIN auth.users uto ON a.assigned_to = uto.id
       LEFT JOIN auth.users uby ON a.assigned_by = uby.id
       ${whereClause}
       ORDER BY a.due_date ASC, a.due_time ASC NULLS LAST`,
      params
    );

    return activities;
  }

  async findById(id: string, tenantId: string): Promise<Activity> {
    const activity = await queryOne<Activity>(
      `SELECT a.*,
              uto.first_name || ' ' || uto.last_name as assigned_to_name,
              uby.first_name || ' ' || uby.last_name as assigned_by_name
       FROM system.activities a
       LEFT JOIN auth.users uto ON a.assigned_to = uto.id
       LEFT JOIN auth.users uby ON a.assigned_by = uby.id
       WHERE a.id = $1 AND a.tenant_id = $2`,
      [id, tenantId]
    );

    if (!activity) {
      throw new NotFoundError('Actividad no encontrada');
    }

    return activity;
  }

  async create(dto: CreateActivityDto, tenantId: string, userId: string): Promise<Activity> {
    const activity = await queryOne<Activity>(
      `INSERT INTO system.activities (
        tenant_id, model, record_id, activity_type, summary, description,
        assigned_to, assigned_by, due_date, due_time, created_by
      )
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $8)
       RETURNING *`,
      [
        tenantId, dto.model, dto.record_id, dto.activity_type,
        dto.summary, dto.description, dto.assigned_to || userId,
        userId, dto.due_date, dto.due_time
      ]
    );

    return activity!;
  }

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

    if (existing.status === 'done' || existing.status === 'cancelled') {
      throw new ValidationError('No se puede editar una actividad completada o cancelada');
    }

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

    if (dto.activity_type !== undefined) {
      updateFields.push(`activity_type = $${paramIndex++}`);
      values.push(dto.activity_type);
    }
    if (dto.summary !== undefined) {
      updateFields.push(`summary = $${paramIndex++}`);
      values.push(dto.summary);
    }
    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.due_date !== undefined) {
      updateFields.push(`due_date = $${paramIndex++}`);
      values.push(dto.due_date);
    }
    if (dto.due_time !== undefined) {
      updateFields.push(`due_time = $${paramIndex++}`);
      values.push(dto.due_time);
    }

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

    values.push(id, tenantId);

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

    return this.findById(id, tenantId);
  }

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

    if (existing.status !== 'planned' && existing.status !== 'overdue') {
      throw new ValidationError('Solo se pueden completar actividades planificadas o vencidas');
    }

    const activity = await queryOne<Activity>(
      `UPDATE system.activities SET
         status = 'done',
         completed_at = CURRENT_TIMESTAMP,
         completed_by = $1
       WHERE id = $2 AND tenant_id = $3
       RETURNING *`,
      [userId, id, tenantId]
    );

    return activity!;
  }

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

    if (existing.status === 'done') {
      throw new ValidationError('No se puede cancelar una actividad completada');
    }

    const activity = await queryOne<Activity>(
      `UPDATE system.activities SET status = 'cancelled'
       WHERE id = $1 AND tenant_id = $2
       RETURNING *`,
      [id, tenantId]
    );

    return activity!;
  }

  async reschedule(id: string, dueDate: string, dueTime: string | null, tenantId: string): Promise<Activity> {
    const existing = await this.findById(id, tenantId);

    if (existing.status === 'done' || existing.status === 'cancelled') {
      throw new ValidationError('No se puede reprogramar una actividad completada o cancelada');
    }

    const activity = await queryOne<Activity>(
      `UPDATE system.activities SET
         due_date = $1,
         due_time = $2,
         status = 'planned'
       WHERE id = $3 AND tenant_id = $4
       RETURNING *`,
      [dueDate, dueTime, id, tenantId]
    );

    return activity!;
  }

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

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

  async markOverdueActivities(tenantId?: string): Promise<number> {
    let whereClause = `WHERE status = 'planned' AND due_date < CURRENT_DATE`;
    const params: any[] = [];

    if (tenantId) {
      whereClause += ' AND tenant_id = $1';
      params.push(tenantId);
    }

    const result = await query(
      `UPDATE system.activities SET status = 'overdue' ${whereClause}`,
      params
    );

    return result.length;
  }
}

export const activitiesService = new ActivitiesService();
