-- ============================================================================
-- SCHEMA: education
-- TABLE: course_reviews
-- DESCRIPTION: Resenas y calificaciones de cursos
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 2 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Tabla de Resenas de Cursos
CREATE TABLE IF NOT EXISTS education.course_reviews (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    course_id UUID NOT NULL REFERENCES education.courses(id) ON DELETE CASCADE,
    enrollment_id UUID NOT NULL REFERENCES education.enrollments(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,

    -- Calificacion
    rating INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5),

    -- Calificaciones detalladas
    rating_content INTEGER CHECK (rating_content BETWEEN 1 AND 5),
    rating_instructor INTEGER CHECK (rating_instructor BETWEEN 1 AND 5),
    rating_value INTEGER CHECK (rating_value BETWEEN 1 AND 5),

    -- Resena
    title VARCHAR(200),
    review_text TEXT,

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'pending'
        CHECK (status IN ('pending', 'approved', 'rejected', 'flagged')),
    rejection_reason TEXT,
    flagged_reason TEXT,

    -- Moderacion
    is_verified_purchase BOOLEAN NOT NULL DEFAULT TRUE,
    is_featured BOOLEAN NOT NULL DEFAULT FALSE,
    moderated_by UUID REFERENCES users.users(id),
    moderated_at TIMESTAMPTZ,

    -- Utilidad
    helpful_count INTEGER NOT NULL DEFAULT 0,
    not_helpful_count INTEGER NOT NULL DEFAULT 0,

    -- Respuesta del instructor
    instructor_response TEXT,
    instructor_response_at TIMESTAMPTZ,

    -- Edicion
    is_edited BOOLEAN NOT NULL DEFAULT FALSE,
    edited_at TIMESTAMPTZ,
    original_review_text TEXT,

    -- Progreso al momento de la resena
    progress_at_review DECIMAL(5, 2),

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

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

    -- Constraints
    CONSTRAINT course_reviews_unique_user UNIQUE (user_id, course_id)
);

COMMENT ON TABLE education.course_reviews IS
'Resenas y calificaciones de cursos por estudiantes';

COMMENT ON COLUMN education.course_reviews.is_verified_purchase IS
'TRUE si el usuario realmente completo/compro el curso';

-- Indices
CREATE INDEX IF NOT EXISTS idx_course_reviews_course
    ON education.course_reviews(course_id, status)
    WHERE status = 'approved';

CREATE INDEX IF NOT EXISTS idx_course_reviews_user
    ON education.course_reviews(user_id);

CREATE INDEX IF NOT EXISTS idx_course_reviews_tenant
    ON education.course_reviews(tenant_id);

CREATE INDEX IF NOT EXISTS idx_course_reviews_rating
    ON education.course_reviews(course_id, rating DESC)
    WHERE status = 'approved';

CREATE INDEX IF NOT EXISTS idx_course_reviews_featured
    ON education.course_reviews(course_id, is_featured)
    WHERE is_featured = TRUE AND status = 'approved';

CREATE INDEX IF NOT EXISTS idx_course_reviews_helpful
    ON education.course_reviews(course_id, helpful_count DESC)
    WHERE status = 'approved';

CREATE INDEX IF NOT EXISTS idx_course_reviews_pending
    ON education.course_reviews(status, created_at)
    WHERE status = 'pending';

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

-- Trigger para actualizar rating promedio del curso
CREATE OR REPLACE FUNCTION education.update_course_rating()
RETURNS TRIGGER AS $$
DECLARE
    v_avg_rating DECIMAL(3, 2);
    v_review_count INTEGER;
BEGIN
    -- Calcular nuevo promedio
    SELECT
        COALESCE(AVG(rating), 0),
        COUNT(*)
    INTO v_avg_rating, v_review_count
    FROM education.course_reviews
    WHERE course_id = COALESCE(NEW.course_id, OLD.course_id)
      AND status = 'approved';

    -- Actualizar curso
    UPDATE education.courses
    SET average_rating = v_avg_rating,
        review_count = v_review_count
    WHERE id = COALESCE(NEW.course_id, OLD.course_id);

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

DROP TRIGGER IF EXISTS course_review_rating ON education.course_reviews;
CREATE TRIGGER course_review_rating
    AFTER INSERT OR UPDATE OF rating, status OR DELETE ON education.course_reviews
    FOR EACH ROW
    EXECUTE FUNCTION education.update_course_rating();

-- Trigger para guardar texto original al editar
CREATE OR REPLACE FUNCTION education.save_original_review()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.review_text IS DISTINCT FROM OLD.review_text AND OLD.original_review_text IS NULL THEN
        NEW.original_review_text := OLD.review_text;
        NEW.is_edited := TRUE;
        NEW.edited_at := NOW();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS course_review_edit ON education.course_reviews;
CREATE TRIGGER course_review_edit
    BEFORE UPDATE OF review_text ON education.course_reviews
    FOR EACH ROW
    EXECUTE FUNCTION education.save_original_review();

-- Tabla auxiliar para votos de utilidad
CREATE TABLE IF NOT EXISTS education.review_votes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    review_id UUID NOT NULL REFERENCES education.course_reviews(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,
    is_helpful BOOLEAN NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    CONSTRAINT review_votes_unique UNIQUE (review_id, user_id)
);

CREATE INDEX IF NOT EXISTS idx_review_votes_review ON education.review_votes(review_id);
CREATE INDEX IF NOT EXISTS idx_review_votes_user ON education.review_votes(user_id);

-- Trigger para actualizar contadores de votos
CREATE OR REPLACE FUNCTION education.update_review_vote_counts()
RETURNS TRIGGER AS $$
BEGIN
    IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
        UPDATE education.course_reviews
        SET helpful_count = (
                SELECT COUNT(*) FROM education.review_votes
                WHERE review_id = NEW.review_id AND is_helpful = TRUE
            ),
            not_helpful_count = (
                SELECT COUNT(*) FROM education.review_votes
                WHERE review_id = NEW.review_id AND is_helpful = FALSE
            )
        WHERE id = NEW.review_id;
    ELSIF TG_OP = 'DELETE' THEN
        UPDATE education.course_reviews
        SET helpful_count = (
                SELECT COUNT(*) FROM education.review_votes
                WHERE review_id = OLD.review_id AND is_helpful = TRUE
            ),
            not_helpful_count = (
                SELECT COUNT(*) FROM education.review_votes
                WHERE review_id = OLD.review_id AND is_helpful = FALSE
            )
        WHERE id = OLD.review_id;
    END IF;

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

DROP TRIGGER IF EXISTS review_vote_counts ON education.review_votes;
CREATE TRIGGER review_vote_counts
    AFTER INSERT OR UPDATE OR DELETE ON education.review_votes
    FOR EACH ROW
    EXECUTE FUNCTION education.update_review_vote_counts();

-- Vista de resenas de un curso
CREATE OR REPLACE VIEW education.v_course_reviews AS
SELECT
    r.id,
    r.course_id,
    r.user_id,
    u.first_name,
    u.last_name,
    u.avatar_url,
    r.rating,
    r.rating_content,
    r.rating_instructor,
    r.rating_value,
    r.title,
    r.review_text,
    r.is_verified_purchase,
    r.is_featured,
    r.helpful_count,
    r.instructor_response,
    r.instructor_response_at,
    r.is_edited,
    r.progress_at_review,
    r.created_at
FROM education.course_reviews r
JOIN users.users u ON r.user_id = u.id
WHERE r.status = 'approved'
ORDER BY r.is_featured DESC, r.helpful_count DESC, r.created_at DESC;

-- Vista de distribucion de ratings
CREATE OR REPLACE VIEW education.v_course_rating_distribution AS
SELECT
    course_id,
    rating,
    COUNT(*) AS count,
    (COUNT(*)::DECIMAL / SUM(COUNT(*)) OVER (PARTITION BY course_id) * 100) AS percentage
FROM education.course_reviews
WHERE status = 'approved'
GROUP BY course_id, rating
ORDER BY course_id, rating DESC;

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

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

ALTER TABLE education.review_votes ENABLE ROW LEVEL SECURITY;

CREATE POLICY review_votes_user_isolation ON education.review_votes
    FOR ALL
    USING (user_id = current_setting('app.current_user_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE ON education.course_reviews TO trading_app;
GRANT SELECT ON education.course_reviews TO trading_readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON education.review_votes TO trading_app;
GRANT SELECT ON education.v_course_reviews TO trading_app;
GRANT SELECT ON education.v_course_rating_distribution TO trading_app;
