-- ============================================================================
-- Migration 032 - Modulo Questionari Fornitori (supply chain Art.21.2.d)
-- ----------------------------------------------------------------------------
-- PROPOSTA DI DESIGN (NON ancora applicata). Da eseguire su host MySQL.
--
-- SOSTITUISCE supplier_questionnaires (mig 027, da qui DEPRECATA/read-only) con
-- un modello completo a template versionati + campagne + domande + risposte:
--
--   supplier_categories            = tassonomia fornitori (preset di sistema + per-org)
--   questionnaire_templates        = modelli di questionario (per-org, opz. per categoria)
--   questionnaire_template_versions= snapshot versionato (questions_snapshot JSON, weight)
--   questionnaire_questions        = domande "live" del template (ordinabili, pesate)
--   questionnaire_campaigns        = invio di un template a un fornitore (sostituisce 027)
--   questionnaire_answers          = risposte puntuali (answer_value JSON-ready)
--
-- Retrocompat: i token sq_ gia inviati (027) restano validi grazie alla
-- colonna access_token_hash (stesso SHA-256 di supplier_questionnaires.token_hash),
-- migrata da mig 035 (migrazione dati). 027 NON viene droppata: resta read-only.
--
-- IMPORTANTE (CLAUDE.md / memoria): MySQL 8 Ubuntu NON supporta
-- "ADD COLUMN IF NOT EXISTS" / "CREATE INDEX IF NOT EXISTS". Le ALTER usano
-- procedure idempotenti su information_schema. CREATE TABLE IF NOT EXISTS ok.
-- DELIMITER non funziona via pipe -> usare "source".
--
-- Eseguire con:
--   mysql -h localhost nis2_agile_db -e "source docs/sql/032_supplier_questionnaires_module.sql"
--
-- Ordine di applicazione: 032 -> 033 -> 034 -> 035 (vedi nota in 035).
-- ============================================================================

-- 1. Categorie fornitori (preset di sistema = organization_id 0)
CREATE TABLE IF NOT EXISTS supplier_categories (
    id              INT NOT NULL AUTO_INCREMENT,
    organization_id INT NOT NULL DEFAULT 0 COMMENT '0 = preset di sistema (visibile a tutte le org)',
    slug            VARCHAR(64) NOT NULL COMMENT 'Identificativo stabile, es. cloud_provider',
    name            VARCHAR(255) NOT NULL,
    description     TEXT NULL,
    is_system       TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 per i preset di sistema',
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uq_supcat_org_slug (organization_id, slug),
    KEY idx_supcat_org (organization_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Tassonomia fornitori: preset di sistema (org 0) + categorie per-org';

-- 2. Template questionario (per-org, opz. legato a una categoria)
CREATE TABLE IF NOT EXISTS questionnaire_templates (
    id              INT NOT NULL AUTO_INCREMENT,
    organization_id INT NOT NULL,
    category_id     INT NULL COMMENT 'FK supplier_categories: template specifico per categoria',
    name            VARCHAR(255) NOT NULL,
    description     TEXT NULL,
    current_version VARCHAR(20) NOT NULL DEFAULT '1.0' COMMENT 'Versione live (vedi questionnaire_template_versions)',
    status          ENUM('draft','active','archived') NOT NULL DEFAULT 'draft',
    pass_threshold  TINYINT UNSIGNED NULL COMMENT 'Soglia score 0-100 per esito positivo',
    is_default      TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Template predefinito per la org/categoria',
    created_by      INT NULL,
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_qt_org (organization_id),
    KEY idx_qt_category (category_id),
    KEY idx_qt_org_status (organization_id, status),
    CONSTRAINT fk_qt_org      FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
    CONSTRAINT fk_qt_category FOREIGN KEY (category_id)     REFERENCES supplier_categories(id) ON DELETE SET NULL,
    CONSTRAINT fk_qt_creator  FOREIGN KEY (created_by)      REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Modelli di questionario di sicurezza fornitori (per organizzazione)';

-- 3. Versioni template (pattern policy_versions, snapshot domande+weight)
CREATE TABLE IF NOT EXISTS questionnaire_template_versions (
    id                 INT NOT NULL AUTO_INCREMENT,
    template_id        INT NOT NULL,
    organization_id    INT NOT NULL COMMENT 'Denormalizzato per filtri/authz',
    version            VARCHAR(20) NOT NULL,
    questions_snapshot JSON NOT NULL COMMENT 'Snapshot completo domande incl. weight, opzioni, tipo',
    change_note        VARCHAR(255) NULL,
    created_by         INT NULL,
    created_at         DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uq_qtv_template_version (template_id, version),
    KEY idx_qtv_template (template_id),
    KEY idx_qtv_org (organization_id),
    CONSTRAINT fk_qtv_template FOREIGN KEY (template_id)     REFERENCES questionnaire_templates(id) ON DELETE CASCADE,
    CONSTRAINT fk_qtv_org      FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
    CONSTRAINT fk_qtv_creator  FOREIGN KEY (created_by)      REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Storico versionato dei template questionario (snapshot domande+weight)';

-- 4. Domande "live" del template (ordinabili, pesate, multi-tipo)
CREATE TABLE IF NOT EXISTS questionnaire_questions (
    id              INT NOT NULL AUTO_INCREMENT,
    template_id     INT NOT NULL,
    organization_id INT NOT NULL COMMENT 'Denormalizzato per filtri/authz',
    question_code   VARCHAR(50) NULL COMMENT 'Codice stabile opzionale, es. gvsc07_a_access_level',
    question_text   TEXT NOT NULL,
    question_type   ENUM('yes_no_partial','single_choice','multi_choice','scale_1_5','text','number','file') NOT NULL DEFAULT 'yes_no_partial',
    options         JSON NULL COMMENT 'Opzioni per single_choice/multi_choice/scale',
    weight          DECIMAL(6,2) NOT NULL DEFAULT 1.00 COMMENT 'Peso nello scoring',
    is_required     TINYINT(1) NOT NULL DEFAULT 1,
    order_index     INT NOT NULL DEFAULT 0 COMMENT 'Ordinamento di presentazione',
    nis2_ref        VARCHAR(40) NULL COMMENT 'Riferimento normativo es. GV.SC-07.a',
    vuln_flag       VARCHAR(64) NULL COMMENT 'Flag vulnerabilita acceso da risposta negativa',
    high_criticality_only TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = mostra solo a fornitori high/critical',
    created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_qq_template_order (template_id, order_index),
    KEY idx_qq_org (organization_id),
    CONSTRAINT fk_qq_template FOREIGN KEY (template_id)     REFERENCES questionnaire_templates(id) ON DELETE CASCADE,
    CONSTRAINT fk_qq_org      FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Domande del template questionario fornitori (ordinabili e pesate)';

-- 5. Campagne (SOSTITUISCE supplier_questionnaires di mig 027)
CREATE TABLE IF NOT EXISTS questionnaire_campaigns (
    id                  INT NOT NULL AUTO_INCREMENT,
    organization_id     INT NOT NULL,
    supplier_id         INT NOT NULL,
    template_id         INT NULL COMMENT 'Template usato (NULL se importata da 027 legacy)',
    template_version    VARCHAR(20) NULL COMMENT 'Versione template congelata all''invio',
    access_token_hash   CHAR(64) NOT NULL COMMENT 'SHA-256 del token sq_ inviato (retrocompat 027)',
    status              ENUM('draft','sent','in_progress','completed','expired','cancelled') NOT NULL DEFAULT 'sent',
    score               INT NULL COMMENT 'Punteggio 0-100 calcolato dalle risposte',
    risk_level          ENUM('low','medium','high','critical') NULL,
    answers             JSON NULL COMMENT 'Cache aggregata risposte (la verita e in questionnaire_answers)',
    show_score_to_supplier TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'F23: default OFF',
    language            ENUM('it','en','auto') NOT NULL DEFAULT 'it' COMMENT 'Lingua portale/email',
    sent_to_email       VARCHAR(255) NULL,
    sent_at             DATETIME NULL,
    due_at              DATETIME NULL COMMENT 'Scadenza compilazione',
    completed_at        DATETIME NULL,
    expires_at          DATETIME NULL,
    reminder_count      INT NOT NULL DEFAULT 0,
    last_reminder_at    DATETIME NULL,
    next_reminder_at    DATETIME NULL COMMENT 'Prossimo invio reminder (cron)',
    reminder_offsets    JSON NULL COMMENT 'Offset reminder, es. [-14,-7,-1]',
    is_recurring        TINYINT(1) NOT NULL DEFAULT 0,
    recurrence_months   TINYINT UNSIGNED NULL COMMENT 'Periodicita in mesi, se ricorrente',
    next_recurrence_at  DATETIME NULL COMMENT 'Prossima riemissione (cron)',
    created_by          INT NULL,
    created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uq_qc_token (access_token_hash),
    KEY idx_qc_org (organization_id),
    KEY idx_qc_supplier (supplier_id),
    KEY idx_qc_template (template_id),
    KEY idx_qc_status_reminder   (status, next_reminder_at),
    KEY idx_qc_status_due        (status, due_at),
    KEY idx_qc_status_recurrence (status, next_recurrence_at),
    CONSTRAINT fk_qc_org      FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
    CONSTRAINT fk_qc_supplier FOREIGN KEY (supplier_id)     REFERENCES suppliers(id) ON DELETE CASCADE,
    CONSTRAINT fk_qc_template FOREIGN KEY (template_id)     REFERENCES questionnaire_templates(id) ON DELETE SET NULL,
    CONSTRAINT fk_qc_creator  FOREIGN KEY (created_by)      REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Campagne questionario inviate ai fornitori (sostituisce supplier_questionnaires/027)';

-- 6. Risposte puntuali (answer_value JSON-ready per multi_choice/file)
CREATE TABLE IF NOT EXISTS questionnaire_answers (
    id              INT NOT NULL AUTO_INCREMENT,
    campaign_id     INT NOT NULL,
    organization_id INT NOT NULL COMMENT 'Denormalizzato NOT NULL per filtri/authz',
    question_id     INT NULL COMMENT 'FK domanda live (NULL se domanda legacy/snapshot)',
    question_code   VARCHAR(50) NULL COMMENT 'Codice domanda dallo snapshot, per storicizzazione',
    answer_value    JSON NULL COMMENT 'Valore risposta JSON-ready (scalare/array per multi_choice/file)',
    answer_text     TEXT NULL COMMENT 'Testo libero',
    file_ref        VARCHAR(255) NULL COMMENT 'Riferimento allegato',
    score_awarded   DECIMAL(6,2) NULL COMMENT 'Punteggio attribuito alla risposta',
    answered_by     INT NULL COMMENT 'supplier_user_id che ha risposto (multi-referente)',
    answered_at     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY uq_qa_campaign_question (campaign_id, question_id),
    KEY idx_qa_org (organization_id),
    KEY idx_qa_question (question_id),
    CONSTRAINT fk_qa_campaign FOREIGN KEY (campaign_id)     REFERENCES questionnaire_campaigns(id) ON DELETE CASCADE,
    CONSTRAINT fk_qa_org      FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
    CONSTRAINT fk_qa_question FOREIGN KEY (question_id)     REFERENCES questionnaire_questions(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Risposte puntuali alle campagne questionario (answer_value JSON-ready, append-only)';

-- 7. Seed idempotente preset di sistema (organization_id = 0) - 10 categorie
INSERT INTO supplier_categories (organization_id, slug, name, description, is_system)
VALUES
    (0, 'cloud_provider',        'Cloud Provider (IaaS/PaaS/SaaS)',           'Fornitori di servizi cloud e hosting',                 1),
    (0, 'managed_security',      'Managed Security / MSSP',                   'SOC, MDR, gestione sicurezza in outsourcing',          1),
    (0, 'connectivity_telco',    'Connettivita / Telco',                      'Operatori di rete, ISP, connettivita',                 1),
    (0, 'hardware_supplier',     'Fornitore Hardware',                        'Apparati di rete, server, dispositivi',                1),
    (0, 'software_vendor',       'Software Vendor / SaaS applicativo',        'Vendor di software applicativo e licenze',             1),
    (0, 'professional_services', 'Servizi Professionali / Consulenza',        'Consulenza IT, legale, audit, compliance',             1),
    (0, 'logistics',             'Logistica e Trasporti',                     'Fornitori di logistica, magazzino, spedizioni',        1),
    (0, 'facility_management',   'Facility Management',                       'Gestione immobili, energia, impianti, sicurezza fisica',1),
    (0, 'datacenter_colocation', 'Data center / Colocation',                  'Data center, housing, colocation, infrastruttura fisica',1),
    (0, 'tic_software_dev',      'TIC / Software Development',                'Sviluppo software su commessa, system integration, ICT', 1)
ON DUPLICATE KEY UPDATE
    name        = VALUES(name),
    description = VALUES(description),
    is_system   = VALUES(is_system),
    updated_at  = CURRENT_TIMESTAMP;

-- ROLLBACK (manuale, ordine inverso per le FK):
-- DROP TABLE IF EXISTS questionnaire_answers;
-- DROP TABLE IF EXISTS questionnaire_campaigns;
-- DROP TABLE IF EXISTS questionnaire_questions;
-- DROP TABLE IF EXISTS questionnaire_template_versions;
-- DROP TABLE IF EXISTS questionnaire_templates;
-- DELETE FROM supplier_categories WHERE organization_id = 0 AND is_system = 1;
-- DROP TABLE IF EXISTS supplier_categories;
