-- =====================================================
-- SCHEMA: projects
-- PROPÓSITO: Gestión de proyectos, tareas, milestones
-- MÓDULOS: MGN-011 (Proyectos Genéricos)
-- FECHA: 2025-11-24
-- =====================================================

-- Crear schema
CREATE SCHEMA IF NOT EXISTS projects;

-- =====================================================
-- TYPES (ENUMs)
-- =====================================================

CREATE TYPE projects.project_status AS ENUM (
  'draft',
  'active',
  'completed',
  'cancelled',
  'on_hold'
);

CREATE TYPE projects.privacy_type AS ENUM (
  'public',
  'private',
  'followers'
);

CREATE TYPE projects.task_status AS ENUM (
  'todo',
  'in_progress',
  'review',
  'done',
  'cancelled'
);

CREATE TYPE projects.task_priority AS ENUM (
  'low',
  'normal',
  'high',
  'urgent'
);

CREATE TYPE projects.dependency_type AS ENUM (
  'finish_to_start',
  'start_to_start',
  'finish_to_finish',
  'start_to_finish'
);

CREATE TYPE projects.milestone_status AS ENUM (
  'pending',
  'completed'
);

-- =====================================================
-- TABLES
-- =====================================================

-- Tabla: projects (Proyectos)
CREATE TABLE projects.projects (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,

  -- Identificación
  name VARCHAR(255) NOT NULL,
  code VARCHAR(50),
  description TEXT,

  -- Responsables
  manager_id UUID REFERENCES auth.users(id),
  partner_id UUID REFERENCES core.partners(id), -- Cliente

  -- Analítica (1-1)
  analytic_account_id UUID REFERENCES analytics.analytic_accounts(id),

  -- Fechas
  date_start DATE,
  date_end DATE,

  -- Estado
  status projects.project_status NOT NULL DEFAULT 'draft',
  privacy projects.privacy_type NOT NULL DEFAULT 'public',

  -- Configuración
  allow_timesheets BOOLEAN DEFAULT TRUE,
  color VARCHAR(20), -- Color para UI

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  deleted_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_projects_code_company UNIQUE (company_id, code),
  CONSTRAINT chk_projects_dates CHECK (date_end IS NULL OR date_end >= date_start)
);

-- Tabla: project_stages (Etapas de tareas)
CREATE TABLE projects.project_stages (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  project_id UUID REFERENCES projects.projects(id) ON DELETE CASCADE,

  name VARCHAR(100) NOT NULL,
  sequence INTEGER NOT NULL DEFAULT 1,
  is_closed BOOLEAN DEFAULT FALSE, -- Etapa final
  fold BOOLEAN DEFAULT FALSE, -- Plegada en kanban

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT chk_project_stages_sequence CHECK (sequence > 0)
);

-- Tabla: tasks (Tareas)
CREATE TABLE projects.tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  project_id UUID NOT NULL REFERENCES projects.projects(id) ON DELETE CASCADE,
  stage_id UUID REFERENCES projects.project_stages(id),

  -- Identificación
  name VARCHAR(255) NOT NULL,
  description TEXT,

  -- Asignación
  assigned_to UUID REFERENCES auth.users(id),
  partner_id UUID REFERENCES core.partners(id),

  -- Jerarquía
  parent_id UUID REFERENCES projects.tasks(id),

  -- Fechas
  date_start DATE,
  date_deadline DATE,

  -- Esfuerzo
  planned_hours DECIMAL(8, 2) DEFAULT 0,
  actual_hours DECIMAL(8, 2) DEFAULT 0,
  progress INTEGER DEFAULT 0, -- 0-100

  -- Prioridad y estado
  priority projects.task_priority NOT NULL DEFAULT 'normal',
  status projects.task_status NOT NULL DEFAULT 'todo',

  -- Milestone
  milestone_id UUID REFERENCES projects.milestones(id),

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  deleted_at TIMESTAMP,
  deleted_by UUID REFERENCES auth.users(id),

  CONSTRAINT chk_tasks_no_self_parent CHECK (id != parent_id),
  CONSTRAINT chk_tasks_dates CHECK (date_deadline IS NULL OR date_deadline >= date_start),
  CONSTRAINT chk_tasks_planned_hours CHECK (planned_hours >= 0),
  CONSTRAINT chk_tasks_actual_hours CHECK (actual_hours >= 0),
  CONSTRAINT chk_tasks_progress CHECK (progress >= 0 AND progress <= 100)
);

-- Tabla: milestones (Hitos)
CREATE TABLE projects.milestones (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  project_id UUID NOT NULL REFERENCES projects.projects(id) ON DELETE CASCADE,

  name VARCHAR(255) NOT NULL,
  description TEXT,
  target_date DATE NOT NULL,
  status projects.milestone_status NOT NULL DEFAULT 'pending',

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  completed_at TIMESTAMP,
  completed_by UUID REFERENCES auth.users(id)
);

-- Tabla: task_dependencies (Dependencias entre tareas)
CREATE TABLE projects.task_dependencies (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  task_id UUID NOT NULL REFERENCES projects.tasks(id) ON DELETE CASCADE,
  depends_on_id UUID NOT NULL REFERENCES projects.tasks(id) ON DELETE CASCADE,

  dependency_type projects.dependency_type NOT NULL DEFAULT 'finish_to_start',

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT uq_task_dependencies UNIQUE (task_id, depends_on_id),
  CONSTRAINT chk_task_dependencies_no_self CHECK (task_id != depends_on_id)
);

-- Tabla: task_tags (Etiquetas de tareas)
CREATE TABLE projects.task_tags (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  name VARCHAR(100) NOT NULL,
  color VARCHAR(20),

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT uq_task_tags_name_tenant UNIQUE (tenant_id, name)
);

-- Tabla: task_tag_assignments (Many-to-many)
CREATE TABLE projects.task_tag_assignments (
  task_id UUID NOT NULL REFERENCES projects.tasks(id) ON DELETE CASCADE,
  tag_id UUID NOT NULL REFERENCES projects.task_tags(id) ON DELETE CASCADE,

  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (task_id, tag_id)
);

-- Tabla: timesheets (Registro de horas)
CREATE TABLE projects.timesheets (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,

  task_id UUID REFERENCES projects.tasks(id) ON DELETE SET NULL,
  project_id UUID NOT NULL REFERENCES projects.projects(id),

  employee_id UUID, -- FK a hr.employees (se crea después)
  user_id UUID REFERENCES auth.users(id),

  -- Fecha y horas
  date DATE NOT NULL,
  hours DECIMAL(8, 2) NOT NULL,

  -- Descripción
  description TEXT,

  -- Analítica
  analytic_account_id UUID REFERENCES analytics.analytic_accounts(id), -- Distribución analítica
  analytic_line_id UUID REFERENCES analytics.analytic_lines(id), -- Línea analítica generada

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),

  CONSTRAINT chk_timesheets_hours CHECK (hours > 0)
);

-- Tabla: task_checklists (Checklists dentro de tareas)
CREATE TABLE projects.task_checklists (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  task_id UUID NOT NULL REFERENCES projects.tasks(id) ON DELETE CASCADE,

  item_name VARCHAR(255) NOT NULL,
  is_completed BOOLEAN DEFAULT FALSE,
  sequence INTEGER DEFAULT 1,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  completed_at TIMESTAMP,
  completed_by UUID REFERENCES auth.users(id)
);

-- Tabla: project_templates (Plantillas de proyectos)
CREATE TABLE projects.project_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  name VARCHAR(255) NOT NULL,
  description TEXT,

  -- Template data (JSON con estructura de proyecto, tareas, etc.)
  template_data JSONB DEFAULT '{}',

  -- Control
  active BOOLEAN DEFAULT TRUE,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_project_templates_name_tenant UNIQUE (tenant_id, name)
);

-- =====================================================
-- INDICES
-- =====================================================

-- Projects
CREATE INDEX idx_projects_tenant_id ON projects.projects(tenant_id);
CREATE INDEX idx_projects_company_id ON projects.projects(company_id);
CREATE INDEX idx_projects_manager_id ON projects.projects(manager_id);
CREATE INDEX idx_projects_partner_id ON projects.projects(partner_id);
CREATE INDEX idx_projects_analytic_account_id ON projects.projects(analytic_account_id);
CREATE INDEX idx_projects_status ON projects.projects(status);

-- Project Stages
CREATE INDEX idx_project_stages_tenant_id ON projects.project_stages(tenant_id);
CREATE INDEX idx_project_stages_project_id ON projects.project_stages(project_id);
CREATE INDEX idx_project_stages_sequence ON projects.project_stages(sequence);

-- Tasks
CREATE INDEX idx_tasks_tenant_id ON projects.tasks(tenant_id);
CREATE INDEX idx_tasks_project_id ON projects.tasks(project_id);
CREATE INDEX idx_tasks_stage_id ON projects.tasks(stage_id);
CREATE INDEX idx_tasks_assigned_to ON projects.tasks(assigned_to);
CREATE INDEX idx_tasks_parent_id ON projects.tasks(parent_id);
CREATE INDEX idx_tasks_milestone_id ON projects.tasks(milestone_id);
CREATE INDEX idx_tasks_status ON projects.tasks(status);
CREATE INDEX idx_tasks_priority ON projects.tasks(priority);
CREATE INDEX idx_tasks_date_deadline ON projects.tasks(date_deadline);

-- Milestones
CREATE INDEX idx_milestones_tenant_id ON projects.milestones(tenant_id);
CREATE INDEX idx_milestones_project_id ON projects.milestones(project_id);
CREATE INDEX idx_milestones_status ON projects.milestones(status);
CREATE INDEX idx_milestones_target_date ON projects.milestones(target_date);

-- Task Dependencies
CREATE INDEX idx_task_dependencies_task_id ON projects.task_dependencies(task_id);
CREATE INDEX idx_task_dependencies_depends_on_id ON projects.task_dependencies(depends_on_id);

-- Timesheets
CREATE INDEX idx_timesheets_tenant_id ON projects.timesheets(tenant_id);
CREATE INDEX idx_timesheets_company_id ON projects.timesheets(company_id);
CREATE INDEX idx_timesheets_task_id ON projects.timesheets(task_id);
CREATE INDEX idx_timesheets_project_id ON projects.timesheets(project_id);
CREATE INDEX idx_timesheets_employee_id ON projects.timesheets(employee_id);
CREATE INDEX idx_timesheets_date ON projects.timesheets(date);
CREATE INDEX idx_timesheets_analytic_account_id ON projects.timesheets(analytic_account_id) WHERE analytic_account_id IS NOT NULL;

-- Task Checklists
CREATE INDEX idx_task_checklists_task_id ON projects.task_checklists(task_id);

-- Project Templates
CREATE INDEX idx_project_templates_tenant_id ON projects.project_templates(tenant_id);
CREATE INDEX idx_project_templates_active ON projects.project_templates(active) WHERE active = TRUE;

-- =====================================================
-- FUNCTIONS
-- =====================================================

-- Función: update_task_actual_hours
CREATE OR REPLACE FUNCTION projects.update_task_actual_hours()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'DELETE' THEN
    UPDATE projects.tasks
    SET actual_hours = (
      SELECT COALESCE(SUM(hours), 0)
      FROM projects.timesheets
      WHERE task_id = OLD.task_id
    )
    WHERE id = OLD.task_id;
  ELSE
    UPDATE projects.tasks
    SET actual_hours = (
      SELECT COALESCE(SUM(hours), 0)
      FROM projects.timesheets
      WHERE task_id = NEW.task_id
    )
    WHERE id = NEW.task_id;
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION projects.update_task_actual_hours IS 'Actualiza las horas reales de una tarea al cambiar timesheets';

-- Función: check_task_dependencies
CREATE OR REPLACE FUNCTION projects.check_task_dependencies(p_task_id UUID)
RETURNS BOOLEAN AS $$
DECLARE
  v_unfinished_count INTEGER;
BEGIN
  SELECT COUNT(*)
  INTO v_unfinished_count
  FROM projects.task_dependencies td
  JOIN projects.tasks t ON td.depends_on_id = t.id
  WHERE td.task_id = p_task_id
    AND t.status != 'done';

  RETURN v_unfinished_count = 0;
END;
$$ LANGUAGE plpgsql STABLE;

COMMENT ON FUNCTION projects.check_task_dependencies IS 'Verifica si todas las dependencias de una tarea están completadas';

-- Función: prevent_circular_dependencies
CREATE OR REPLACE FUNCTION projects.prevent_circular_dependencies()
RETURNS TRIGGER AS $$
BEGIN
  -- Verificar si crear esta dependencia crea un ciclo
  IF EXISTS (
    WITH RECURSIVE dep_chain AS (
      SELECT task_id, depends_on_id
      FROM projects.task_dependencies
      WHERE task_id = NEW.depends_on_id

      UNION ALL

      SELECT td.task_id, td.depends_on_id
      FROM projects.task_dependencies td
      JOIN dep_chain dc ON td.task_id = dc.depends_on_id
    )
    SELECT 1 FROM dep_chain WHERE depends_on_id = NEW.task_id
  ) THEN
    RAISE EXCEPTION 'Cannot create circular dependency';
  END IF;

  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION projects.prevent_circular_dependencies IS 'Previene la creación de dependencias circulares entre tareas';

-- =====================================================
-- TRIGGERS
-- =====================================================

CREATE TRIGGER trg_projects_updated_at
  BEFORE UPDATE ON projects.projects
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_tasks_updated_at
  BEFORE UPDATE ON projects.tasks
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_milestones_updated_at
  BEFORE UPDATE ON projects.milestones
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_timesheets_updated_at
  BEFORE UPDATE ON projects.timesheets
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_project_templates_updated_at
  BEFORE UPDATE ON projects.project_templates
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

-- Trigger: Actualizar horas reales de tarea al cambiar timesheet
CREATE TRIGGER trg_timesheets_update_task_hours
  AFTER INSERT OR UPDATE OR DELETE ON projects.timesheets
  FOR EACH ROW
  EXECUTE FUNCTION projects.update_task_actual_hours();

-- Trigger: Prevenir dependencias circulares
CREATE TRIGGER trg_task_dependencies_prevent_circular
  BEFORE INSERT ON projects.task_dependencies
  FOR EACH ROW
  EXECUTE FUNCTION projects.prevent_circular_dependencies();

-- =====================================================
-- TRACKING AUTOMÁTICO (mail.thread pattern)
-- =====================================================

-- Trigger: Tracking automático para proyectos
CREATE TRIGGER track_project_changes
  AFTER INSERT OR UPDATE OR DELETE ON projects.projects
  FOR EACH ROW EXECUTE FUNCTION system.track_field_changes();

COMMENT ON TRIGGER track_project_changes ON projects.projects IS
'Registra automáticamente cambios en proyectos (estado, nombre, responsable, fechas)';

-- =====================================================
-- ROW LEVEL SECURITY (RLS)
-- =====================================================

ALTER TABLE projects.projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects.project_stages ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects.tasks ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects.milestones ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects.task_tags ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects.timesheets ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects.project_templates ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_projects ON projects.projects
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_project_stages ON projects.project_stages
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_tasks ON projects.tasks
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_milestones ON projects.milestones
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_task_tags ON projects.task_tags
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_timesheets ON projects.timesheets
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_project_templates ON projects.project_templates
  USING (tenant_id = get_current_tenant_id());

-- =====================================================
-- COMENTARIOS
-- =====================================================

COMMENT ON SCHEMA projects IS 'Schema de gestión de proyectos, tareas y timesheets';
COMMENT ON TABLE projects.projects IS 'Proyectos genéricos con tracking de tareas';
COMMENT ON TABLE projects.project_stages IS 'Etapas/columnas para tablero Kanban de tareas';
COMMENT ON TABLE projects.tasks IS 'Tareas dentro de proyectos con jerarquía y dependencias';
COMMENT ON TABLE projects.milestones IS 'Hitos importantes en proyectos';
COMMENT ON TABLE projects.task_dependencies IS 'Dependencias entre tareas (precedencia)';
COMMENT ON TABLE projects.task_tags IS 'Etiquetas para categorizar tareas';
COMMENT ON TABLE projects.timesheets IS 'Registro de horas trabajadas en tareas';
COMMENT ON TABLE projects.task_checklists IS 'Checklists dentro de tareas';
COMMENT ON TABLE projects.project_templates IS 'Plantillas de proyectos para reutilización';

-- =====================================================
-- FIN DEL SCHEMA PROJECTS
-- =====================================================
