-- ============================================================
-- Extension de la base PTA pour l'interface de gestion planning
-- À exécuter APRES pta_mysql_test.sql
-- Compatible MySQL 8+
-- ============================================================

USE pta_test;

-- 1. Motifs d'affectation
CREATE TABLE IF NOT EXISTS motif_planning (
    id_motif INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    code VARCHAR(50) NOT NULL UNIQUE,
    libelle VARCHAR(120) NOT NULL,
    compte_dans_quota BOOLEAN NOT NULL DEFAULT FALSE,
    ordre_affichage SMALLINT UNSIGNED NOT NULL DEFAULT 100,
    actif BOOLEAN NOT NULL DEFAULT TRUE
) ENGINE = InnoDB;

INSERT INTO motif_planning (code, libelle, compte_dans_quota, ordre_affichage)
VALUES
    ('TRAVAIL', 'Heure de travail', TRUE, 10),
    ('CONGE', 'Congé', FALSE, 20),
    ('MALADIE', 'Absence', TRUE, 30),
    ('FORMATION', 'Formation', TRUE, 40),
    ('AUTRE', 'Autre absence (non décomptée)', FALSE, 100)
ON DUPLICATE KEY UPDATE
    libelle = VALUES(libelle),
    compte_dans_quota = VALUES(compte_dans_quota),
    ordre_affichage = VALUES(ordre_affichage),
    actif = TRUE;

-- 2. Rattachement des agents aux structures
CREATE TABLE IF NOT EXISTS agent_structure (
    id_agent_structure INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    id_agent INT UNSIGNED NOT NULL,
    id_structure INT UNSIGNED NOT NULL,
    date_debut DATE NULL,
    date_fin DATE NULL,
    actif BOOLEAN NOT NULL DEFAULT TRUE,

    CONSTRAINT fk_agent_structure_agent
        FOREIGN KEY (id_agent) REFERENCES agent(id_agent),

    CONSTRAINT fk_agent_structure_structure
        FOREIGN KEY (id_structure) REFERENCES structure(id_structure),

    CONSTRAINT chk_agent_structure_dates
        CHECK (date_fin IS NULL OR date_debut IS NULL OR date_fin >= date_debut),

    UNIQUE KEY uk_agent_structure_periode (id_agent, id_structure, date_debut)
) ENGINE = InnoDB;

-- Jeu d'essai : un seul rattachement principal par agent.
INSERT INTO agent_structure (id_agent, id_structure, date_debut, date_fin, actif)
VALUES
    (1, 1, '2026-01-01', NULL, TRUE),
    (2, 1, '2026-01-01', NULL, TRUE),
    (3, 2, '2026-01-01', NULL, TRUE)
ON DUPLICATE KEY UPDATE
    date_fin = VALUES(date_fin),
    actif = VALUES(actif);

-- 3. Ajout du motif à chaque créneau existant
SET @travail_id = (
    SELECT id_motif FROM motif_planning WHERE code = 'TRAVAIL' LIMIT 1
);

SET @col_exists = (
    SELECT COUNT(*)
    FROM information_schema.COLUMNS
    WHERE TABLE_SCHEMA = DATABASE()
      AND TABLE_NAME = 'creneau_horaire'
      AND COLUMN_NAME = 'id_motif'
);

SET @sql = IF(
    @col_exists = 0,
    'ALTER TABLE creneau_horaire ADD COLUMN id_motif INT UNSIGNED NULL AFTER id_planning',
    'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

UPDATE creneau_horaire
SET id_motif = @travail_id
WHERE id_motif IS NULL;

SET @fk_exists = (
    SELECT COUNT(*)
    FROM information_schema.TABLE_CONSTRAINTS
    WHERE CONSTRAINT_SCHEMA = DATABASE()
      AND TABLE_NAME = 'creneau_horaire'
      AND CONSTRAINT_NAME = 'fk_creneau_motif'
      AND CONSTRAINT_TYPE = 'FOREIGN KEY'
);

SET @sql = IF(
    @fk_exists = 0,
    'ALTER TABLE creneau_horaire ADD CONSTRAINT fk_creneau_motif FOREIGN KEY (id_motif) REFERENCES motif_planning(id_motif)',
    'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

ALTER TABLE creneau_horaire
    MODIFY id_motif INT UNSIGNED NOT NULL;

SET @idx_exists = (
    SELECT COUNT(*)
    FROM information_schema.STATISTICS
    WHERE TABLE_SCHEMA = DATABASE()
      AND TABLE_NAME = 'creneau_horaire'
      AND INDEX_NAME = 'idx_creneau_motif'
);

SET @sql = IF(
    @idx_exists = 0,
    'CREATE INDEX idx_creneau_motif ON creneau_horaire(id_motif)',
    'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

-- 4. Vue de synthèse par agent en tenant compte du motif
CREATE OR REPLACE VIEW v_suivi_quota_contrat AS
SELECT
    ca.id_contrat,
    ca.id_agent,
    a.matricule,
    CONCAT(a.prenom, ' ', a.nom) AS agent,
    ca.date_debut,
    ca.date_fin,
    ca.quota_minutes,
    ROUND(ca.quota_minutes / 60, 2) AS quota_heures,
    COALESCE(
        SUM(
            CASE
                WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
                THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
                ELSE 0
            END
        ),
        0
    ) AS minutes_planifiees_validees,
    ROUND(
        COALESCE(
            SUM(
                CASE
                    WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
                    THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
                    ELSE 0
                END
            ),
            0
        ) / 60,
        2
    ) AS heures_planifiees_validees,
    ROUND(
        (
            ca.quota_minutes
            - COALESCE(
                SUM(
                    CASE
                        WHEN p.statut = 'VALIDE' AND m.compte_dans_quota = TRUE
                        THEN TIME_TO_SEC(TIMEDIFF(c.heure_fin, c.heure_debut)) / 60
                        ELSE 0
                    END
                ),
                0
            )
        ) / 60,
        2
    ) AS heures_restantes
FROM contrat_agent ca
JOIN agent a ON a.id_agent = ca.id_agent
LEFT JOIN planning p ON p.id_agent = ca.id_agent
LEFT JOIN creneau_horaire c
    ON c.id_planning = p.id_planning
   AND c.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
LEFT JOIN motif_planning m ON m.id_motif = c.id_motif
GROUP BY
    ca.id_contrat,
    ca.id_agent,
    a.matricule,
    a.prenom,
    a.nom,
    ca.date_debut,
    ca.date_fin,
    ca.quota_minutes;


-- 5. Mise à jour du contrôle de quota : seuls les motifs marqués
-- compte_dans_quota = TRUE sont additionnés.
DELIMITER $$

DROP PROCEDURE IF EXISTS sp_preparer_controles_planning$$
CREATE PROCEDURE sp_preparer_controles_planning(
    IN p_id_planning INT UNSIGNED
)
controle: BEGIN
    DECLARE v_planning_existe INT DEFAULT 0;

    DROP TEMPORARY TABLE IF EXISTS tmp_controles_planning;

    CREATE TEMPORARY TABLE tmp_controles_planning (
        id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        niveau ENUM('ERREUR', 'ALERTE', 'INFO') NOT NULL,
        code VARCHAR(100) NOT NULL,
        message VARCHAR(1000) NOT NULL
    );

    SELECT COUNT(*)
    INTO v_planning_existe
    FROM planning
    WHERE id_planning = p_id_planning;

    IF v_planning_existe = 0 THEN
        INSERT INTO tmp_controles_planning(niveau, code, message)
        VALUES (
            'ERREUR',
            'PLANNING_INTROUVABLE',
            CONCAT('Le planning #', p_id_planning, ' n''existe pas.')
        );

        LEAVE controle;
    END IF;

    -- --------------------------------------------------------
    -- Controle 1 : le planning doit contenir au moins un creneau
    -- --------------------------------------------------------
    IF NOT EXISTS (
        SELECT 1
        FROM creneau_horaire
        WHERE id_planning = p_id_planning
    ) THEN
        INSERT INTO tmp_controles_planning(niveau, code, message)
        VALUES (
            'ERREUR',
            'AUCUN_CRENEAU',
            'Le planning ne contient aucun creneau horaire.'
        );
    END IF;

    -- --------------------------------------------------------
    -- Controle 2 : chaque creneau doit etre dans la semaine
    -- --------------------------------------------------------
    INSERT INTO tmp_controles_planning(niveau, code, message)
    SELECT
        'ERREUR',
        'CRENEAU_HORS_SEMAINE',
        CONCAT(
            'Le creneau #', c.id_creneau,
            ' du ', DATE_FORMAT(c.date_jour, '%d/%m/%Y'),
            ' est en dehors de la semaine ', s.numero_semaine,
            ' (', DATE_FORMAT(s.date_debut, '%d/%m/%Y'),
            ' au ', DATE_FORMAT(s.date_fin, '%d/%m/%Y'), ').'
        )
    FROM planning p
    JOIN semaine s
        ON s.id_semaine = p.id_semaine
    JOIN creneau_horaire c
        ON c.id_planning = p.id_planning
    WHERE p.id_planning = p_id_planning
      AND c.date_jour NOT BETWEEN s.date_debut AND s.date_fin;

    -- --------------------------------------------------------
    -- Controle 3 : chaque creneau doit etre couvert par un contrat
    -- --------------------------------------------------------
    INSERT INTO tmp_controles_planning(niveau, code, message)
    SELECT
        'ERREUR',
        'HORS_CONTRAT',
        CONCAT(
            'Le creneau #', c.id_creneau,
            ' du ', DATE_FORMAT(c.date_jour, '%d/%m/%Y'),
            ' n''est couvert par aucun contrat actif pour cet agent.'
        )
    FROM planning p
    JOIN creneau_horaire c
        ON c.id_planning = p.id_planning
    WHERE p.id_planning = p_id_planning
      AND NOT EXISTS (
          SELECT 1
          FROM contrat_agent ca
          WHERE ca.id_agent = p.id_agent
            AND ca.actif = TRUE
            AND c.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
      );

    -- --------------------------------------------------------
    -- Controle 4 : chevauchements dans le meme planning
    -- --------------------------------------------------------
    INSERT INTO tmp_controles_planning(niveau, code, message)
    SELECT
        'ERREUR',
        'CHEVAUCHEMENT_INTERNE',
        CONCAT(
            'Les creneaux #', c1.id_creneau,
            ' et #', c2.id_creneau,
            ' se chevauchent le ', DATE_FORMAT(c1.date_jour, '%d/%m/%Y'),
            ' (', TIME_FORMAT(c1.heure_debut, '%H:%i'), '-', TIME_FORMAT(c1.heure_fin, '%H:%i'),
            ' et ', TIME_FORMAT(c2.heure_debut, '%H:%i'), '-', TIME_FORMAT(c2.heure_fin, '%H:%i'), ').'
        )
    FROM creneau_horaire c1
    JOIN creneau_horaire c2
        ON c2.id_planning = c1.id_planning
        AND c2.date_jour = c1.date_jour
        AND c2.id_creneau > c1.id_creneau
        AND c1.heure_debut < c2.heure_fin
        AND c1.heure_fin > c2.heure_debut
    WHERE c1.id_planning = p_id_planning;

    -- --------------------------------------------------------
    -- Controle 5 : chevauchement avec un autre planning valide
    -- pour le meme agent, meme si la structure est differente.
    -- --------------------------------------------------------
    INSERT INTO tmp_controles_planning(niveau, code, message)
    SELECT
        'ERREUR',
        'CHEVAUCHEMENT_AUTRE_PLANNING',
        CONCAT(
            'Le creneau #', c1.id_creneau,
            ' chevauche le creneau #', c2.id_creneau,
            ' du planning valide #', p2.id_planning,
            ' le ', DATE_FORMAT(c1.date_jour, '%d/%m/%Y'), '.'
        )
    FROM planning p1
    JOIN creneau_horaire c1
        ON c1.id_planning = p1.id_planning
    JOIN planning p2
        ON p2.id_agent = p1.id_agent
        AND p2.id_planning <> p1.id_planning
        AND p2.statut = 'VALIDE'
    JOIN creneau_horaire c2
        ON c2.id_planning = p2.id_planning
        AND c2.date_jour = c1.date_jour
        AND c1.heure_debut < c2.heure_fin
        AND c1.heure_fin > c2.heure_debut
    WHERE p1.id_planning = p_id_planning;

    -- --------------------------------------------------------
    -- Controle 6 : depassement du quota du contrat
    -- Le quota est ici un quota total sur la periode du contrat.
    -- Il s'agit d'une ALERTE et non d'une erreur bloquante.
    -- --------------------------------------------------------
    INSERT INTO tmp_controles_planning(niveau, code, message)
    SELECT
        'ALERTE',
        'QUOTA_DEPASSE',
        CONCAT(
            'Le contrat #', ca.id_contrat,
            ' prevoit ', ROUND(ca.quota_minutes / 60, 2), ' h. ',
            'Apres validation, ',
            ROUND(
                (
                    -- Minutes deja validees, hors planning courant
                    COALESCE((
                        SELECT SUM(
                            TIME_TO_SEC(TIMEDIFF(cv.heure_fin, cv.heure_debut)) / 60
                        )
                        FROM planning pv
                        JOIN creneau_horaire cv
                            ON cv.id_planning = pv.id_planning
                        JOIN motif_planning mv
                            ON mv.id_motif = cv.id_motif
                           AND mv.compte_dans_quota = TRUE
                        WHERE pv.id_agent = ca.id_agent
                          AND pv.statut = 'VALIDE'
                          AND pv.id_planning <> p_id_planning
                          AND cv.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
                    ), 0)
                    +
                    -- Minutes du planning courant appartenant a ce contrat
                    COALESCE((
                        SELECT SUM(
                            TIME_TO_SEC(TIMEDIFF(cc.heure_fin, cc.heure_debut)) / 60
                        )
                        FROM creneau_horaire cc
                        JOIN motif_planning mc
                            ON mc.id_motif = cc.id_motif
                           AND mc.compte_dans_quota = TRUE
                        WHERE cc.id_planning = p_id_planning
                          AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
                    ), 0)
                ) / 60,
                2
            ),
            ' h seraient planifiees.'
        )
    FROM planning p
    JOIN contrat_agent ca
        ON ca.id_agent = p.id_agent
        AND ca.actif = TRUE
    WHERE p.id_planning = p_id_planning
      AND EXISTS (
          SELECT 1
          FROM creneau_horaire cc
          WHERE cc.id_planning = p_id_planning
            AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
      )
      AND (
          COALESCE((
              SELECT SUM(
                  TIME_TO_SEC(TIMEDIFF(cv.heure_fin, cv.heure_debut)) / 60
              )
              FROM planning pv
              JOIN creneau_horaire cv
                  ON cv.id_planning = pv.id_planning
              JOIN motif_planning mv
                  ON mv.id_motif = cv.id_motif
                 AND mv.compte_dans_quota = TRUE
              WHERE pv.id_agent = ca.id_agent
                AND pv.statut = 'VALIDE'
                AND pv.id_planning <> p_id_planning
                AND cv.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
          ), 0)
          +
          COALESCE((
              SELECT SUM(
                  TIME_TO_SEC(TIMEDIFF(cc.heure_fin, cc.heure_debut)) / 60
              )
              FROM creneau_horaire cc
              JOIN motif_planning mc
                  ON mc.id_motif = cc.id_motif
                 AND mc.compte_dans_quota = TRUE
              WHERE cc.id_planning = p_id_planning
                AND cc.date_jour BETWEEN ca.date_debut AND COALESCE(ca.date_fin, '9999-12-31')
          ), 0)
      ) > ca.quota_minutes;

    -- Si aucun probleme n'a ete detecte, on retourne explicitement OK.
    IF NOT EXISTS (
        SELECT 1
        FROM tmp_controles_planning
    ) THEN
        INSERT INTO tmp_controles_planning(niveau, code, message)
        VALUES (
            'INFO',
            'OK',
            'Aucune anomalie detectee. Le planning peut etre valide.'
        );
    END IF;
END$$


DELIMITER ;
