-- ============================================================================
-- SCHEMA: education
-- TABLE: quiz_questions
-- DESCRIPTION: Preguntas de cuestionarios
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 2 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Enum para tipo de pregunta
DO $$ BEGIN
    CREATE TYPE education.question_type AS ENUM (
        'single_choice',        -- Una respuesta correcta
        'multiple_choice',      -- Multiples respuestas correctas
        'true_false',           -- Verdadero/Falso
        'fill_blank',           -- Completar espacio en blanco
        'matching',             -- Emparejar columnas
        'ordering',             -- Ordenar elementos
        'short_answer',         -- Respuesta corta (texto)
        'essay'                 -- Respuesta larga (evaluacion manual)
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Preguntas
CREATE TABLE IF NOT EXISTS education.quiz_questions (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    quiz_id UUID NOT NULL REFERENCES education.quizzes(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,

    -- Pregunta
    type education.question_type NOT NULL DEFAULT 'single_choice',
    question_text TEXT NOT NULL,
    question_html TEXT,                             -- Version con formato
    question_image_url TEXT,

    -- Opciones de respuesta (para choice questions)
    -- Array de objetos: [{ "id": "a", "text": "...", "is_correct": true, "explanation": "..." }]
    options JSONB DEFAULT '[]'::JSONB,

    -- Respuesta correcta (para otros tipos)
    correct_answer TEXT,                            -- Para fill_blank, short_answer
    correct_answers TEXT[],                         -- Para multiple answers
    answer_pattern VARCHAR(255),                    -- Regex para validar respuesta

    -- Para matching questions
    -- [{ "left": "A", "right": "1" }, { "left": "B", "right": "2" }]
    matching_pairs JSONB,

    -- Para ordering questions
    correct_order TEXT[],

    -- Explicacion
    explanation TEXT,
    explanation_html TEXT,
    explanation_video_url TEXT,

    -- Configuracion
    points INTEGER NOT NULL DEFAULT 1,
    partial_credit BOOLEAN NOT NULL DEFAULT FALSE,  -- Credito parcial en multiple choice
    case_sensitive BOOLEAN NOT NULL DEFAULT FALSE,  -- Para respuestas de texto

    -- Dificultad
    difficulty education.difficulty_level DEFAULT 'intermediate',

    -- Orden
    sequence_number INTEGER NOT NULL DEFAULT 0,

    -- Estado
    is_active BOOLEAN NOT NULL DEFAULT TRUE,

    -- Tags para filtrado
    tags VARCHAR(50)[],
    topic VARCHAR(100),

    -- Estadisticas (cache)
    attempt_count INTEGER NOT NULL DEFAULT 0,
    correct_count INTEGER NOT NULL DEFAULT 0,
    average_time_seconds INTEGER,

    -- Metadata
    metadata JSONB DEFAULT '{}'::JSONB,

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT quiz_questions_points_check CHECK (points > 0)
);

COMMENT ON TABLE education.quiz_questions IS
'Preguntas individuales que componen un quiz';

COMMENT ON COLUMN education.quiz_questions.options IS
'Array de opciones para preguntas de seleccion: [{"id":"a","text":"...","is_correct":true}]';

-- Indices
CREATE INDEX IF NOT EXISTS idx_quiz_questions_quiz
    ON education.quiz_questions(quiz_id, sequence_number);

CREATE INDEX IF NOT EXISTS idx_quiz_questions_tenant
    ON education.quiz_questions(tenant_id);

CREATE INDEX IF NOT EXISTS idx_quiz_questions_type
    ON education.quiz_questions(type);

CREATE INDEX IF NOT EXISTS idx_quiz_questions_difficulty
    ON education.quiz_questions(difficulty);

CREATE INDEX IF NOT EXISTS idx_quiz_questions_active
    ON education.quiz_questions(quiz_id, is_active)
    WHERE is_active = TRUE;

-- GIN index para tags
CREATE INDEX IF NOT EXISTS idx_quiz_questions_tags_gin
    ON education.quiz_questions USING GIN (tags);

-- GIN index para options (buscar por contenido)
CREATE INDEX IF NOT EXISTS idx_quiz_questions_options_gin
    ON education.quiz_questions USING GIN (options);

-- Trigger para updated_at
DROP TRIGGER IF EXISTS quiz_question_updated_at ON education.quiz_questions;
CREATE TRIGGER quiz_question_updated_at
    BEFORE UPDATE ON education.quiz_questions
    FOR EACH ROW
    EXECUTE FUNCTION education.update_education_timestamp();

-- Trigger para actualizar question_count en quiz
CREATE OR REPLACE FUNCTION education.update_question_count()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'INSERT' THEN
        UPDATE education.quizzes
        SET question_count = question_count + 1
        WHERE id = NEW.quiz_id;
    ELSIF TG_OP = 'DELETE' THEN
        UPDATE education.quizzes
        SET question_count = question_count - 1
        WHERE id = OLD.quiz_id;
    END IF;

    RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS question_count ON education.quiz_questions;
CREATE TRIGGER question_count
    AFTER INSERT OR DELETE ON education.quiz_questions
    FOR EACH ROW
    EXECUTE FUNCTION education.update_question_count();

-- Funcion para validar respuesta
CREATE OR REPLACE FUNCTION education.validate_answer(
    p_question_id UUID,
    p_answer TEXT
)
RETURNS TABLE (
    is_correct BOOLEAN,
    points_earned INTEGER,
    correct_answer TEXT,
    explanation TEXT
) AS $$
DECLARE
    v_question RECORD;
    v_is_correct BOOLEAN := FALSE;
    v_points INTEGER := 0;
BEGIN
    SELECT * INTO v_question
    FROM education.quiz_questions
    WHERE id = p_question_id;

    IF v_question IS NULL THEN
        RETURN;
    END IF;

    CASE v_question.type
        WHEN 'single_choice' THEN
            -- Verificar si la opcion seleccionada es correcta
            SELECT (opt->>'is_correct')::BOOLEAN INTO v_is_correct
            FROM jsonb_array_elements(v_question.options) AS opt
            WHERE opt->>'id' = p_answer;

        WHEN 'true_false' THEN
            v_is_correct := LOWER(p_answer) = LOWER(v_question.correct_answer);

        WHEN 'fill_blank', 'short_answer' THEN
            IF v_question.case_sensitive THEN
                v_is_correct := p_answer = v_question.correct_answer;
            ELSE
                v_is_correct := LOWER(p_answer) = LOWER(v_question.correct_answer);
            END IF;

            -- Verificar patron regex si existe
            IF NOT v_is_correct AND v_question.answer_pattern IS NOT NULL THEN
                v_is_correct := p_answer ~ v_question.answer_pattern;
            END IF;

        ELSE
            -- Para tipos complejos, retornar NULL (requiere evaluacion especial)
            v_is_correct := NULL;
    END CASE;

    IF v_is_correct THEN
        v_points := v_question.points;
    END IF;

    RETURN QUERY SELECT
        v_is_correct,
        v_points,
        v_question.correct_answer,
        v_question.explanation;
END;
$$ LANGUAGE plpgsql;

-- Vista de preguntas de un quiz (sin respuestas correctas para estudiantes)
CREATE OR REPLACE VIEW education.v_quiz_questions_student AS
SELECT
    q.id,
    q.quiz_id,
    q.type,
    q.question_text,
    q.question_html,
    q.question_image_url,
    -- Remover is_correct de las opciones
    (SELECT jsonb_agg(
        jsonb_build_object(
            'id', opt->>'id',
            'text', opt->>'text'
        )
    ) FROM jsonb_array_elements(q.options) AS opt) AS options,
    q.points,
    q.sequence_number,
    q.difficulty
FROM education.quiz_questions q
WHERE q.is_active = TRUE
ORDER BY q.quiz_id, q.sequence_number;

-- Vista de preguntas con estadisticas (para instructores)
CREATE OR REPLACE VIEW education.v_quiz_questions_admin AS
SELECT
    q.*,
    CASE WHEN q.attempt_count > 0
         THEN (q.correct_count::DECIMAL / q.attempt_count * 100)
         ELSE 0
    END AS success_rate
FROM education.quiz_questions q
ORDER BY q.quiz_id, q.sequence_number;

-- RLS Policy para multi-tenancy
ALTER TABLE education.quiz_questions ENABLE ROW LEVEL SECURITY;

CREATE POLICY quiz_questions_tenant_isolation ON education.quiz_questions
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON education.quiz_questions TO trading_app;
GRANT SELECT ON education.quiz_questions TO trading_readonly;
GRANT SELECT ON education.v_quiz_questions_student TO trading_app;
GRANT SELECT ON education.v_quiz_questions_admin TO trading_app;
GRANT EXECUTE ON FUNCTION education.validate_answer TO trading_app;
