-- ============================================================================
-- SCHEMA: education
-- TABLE: certificates
-- DESCRIPTION: Certificados de finalizacion de cursos
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 2 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Enum para tipo de certificado
DO $$ BEGIN
    CREATE TYPE education.certificate_type AS ENUM (
        'completion',           -- Certificado de finalizacion
        'achievement',          -- Certificado de logro especifico
        'participation',        -- Certificado de participacion
        'excellence',           -- Certificado de excelencia (alta puntuacion)
        'professional'          -- Certificacion profesional
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Certificados
CREATE TABLE IF NOT EXISTS education.certificates (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    certificate_number VARCHAR(50) NOT NULL UNIQUE, -- Numero unico verificable
    enrollment_id UUID NOT NULL REFERENCES education.enrollments(id) ON DELETE CASCADE,
    course_id UUID NOT NULL REFERENCES education.courses(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,

    -- Tipo
    type education.certificate_type NOT NULL DEFAULT 'completion',

    -- Datos del certificado
    title VARCHAR(200) NOT NULL,
    description TEXT,
    recipient_name VARCHAR(200) NOT NULL,           -- Nombre como aparece en certificado

    -- Logro
    final_score DECIMAL(5, 2),
    grade VARCHAR(2),
    completion_date DATE NOT NULL DEFAULT CURRENT_DATE,
    hours_completed INTEGER,

    -- Template y diseño
    template_id UUID,
    template_data JSONB DEFAULT '{}'::JSONB,        -- Datos para el template
    background_url TEXT,

    -- Archivos generados
    pdf_url TEXT,
    image_url TEXT,
    linkedin_url TEXT,                              -- URL para compartir en LinkedIn

    -- Verificacion
    verification_code VARCHAR(20) NOT NULL,         -- Codigo corto para verificar
    verification_url TEXT,
    qr_code_url TEXT,

    -- Blockchain (opcional)
    blockchain_hash VARCHAR(66),                    -- Hash de verificacion en blockchain
    blockchain_network VARCHAR(50),
    blockchain_tx_id VARCHAR(66),

    -- Firmantes
    signed_by JSONB DEFAULT '[]'::JSONB,           -- [{ "name": "...", "title": "...", "signature_url": "..." }]

    -- Expiracion (para certificaciones profesionales)
    expires_at TIMESTAMPTZ,
    is_expired BOOLEAN NOT NULL DEFAULT FALSE,
    renewal_available BOOLEAN NOT NULL DEFAULT FALSE,

    -- Estado
    is_valid BOOLEAN NOT NULL DEFAULT TRUE,
    revoked_at TIMESTAMPTZ,
    revocation_reason TEXT,

    -- Compartido
    is_public BOOLEAN NOT NULL DEFAULT FALSE,
    public_url TEXT,
    share_count INTEGER NOT NULL DEFAULT 0,
    view_count INTEGER NOT NULL DEFAULT 0,

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

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

    -- Constraints
    CONSTRAINT certificates_unique_enrollment UNIQUE (enrollment_id, type)
);

COMMENT ON TABLE education.certificates IS
'Certificados de finalizacion emitidos a estudiantes';

COMMENT ON COLUMN education.certificates.certificate_number IS
'Numero unico para verificacion publica del certificado';

COMMENT ON COLUMN education.certificates.verification_code IS
'Codigo corto (6-8 caracteres) para verificacion rapida';

-- Indices
CREATE INDEX IF NOT EXISTS idx_certificates_enrollment
    ON education.certificates(enrollment_id);

CREATE INDEX IF NOT EXISTS idx_certificates_course
    ON education.certificates(course_id);

CREATE INDEX IF NOT EXISTS idx_certificates_user
    ON education.certificates(user_id);

CREATE INDEX IF NOT EXISTS idx_certificates_tenant
    ON education.certificates(tenant_id);

CREATE INDEX IF NOT EXISTS idx_certificates_number
    ON education.certificates(certificate_number);

CREATE INDEX IF NOT EXISTS idx_certificates_verification
    ON education.certificates(verification_code);

CREATE INDEX IF NOT EXISTS idx_certificates_type
    ON education.certificates(type);

CREATE INDEX IF NOT EXISTS idx_certificates_valid
    ON education.certificates(is_valid)
    WHERE is_valid = TRUE;

CREATE INDEX IF NOT EXISTS idx_certificates_public
    ON education.certificates(is_public, issued_at DESC)
    WHERE is_public = TRUE;

CREATE INDEX IF NOT EXISTS idx_certificates_expires
    ON education.certificates(expires_at)
    WHERE expires_at IS NOT NULL AND is_valid = TRUE;

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

-- Funcion para generar numero de certificado
CREATE OR REPLACE FUNCTION education.generate_certificate_number()
RETURNS TEXT AS $$
DECLARE
    v_year TEXT := TO_CHAR(NOW(), 'YYYY');
    v_seq TEXT;
BEGIN
    -- Formato: CERT-YYYY-XXXXXX (ej: CERT-2026-000123)
    SELECT LPAD(COALESCE(MAX(
        NULLIF(SUBSTRING(certificate_number FROM 11), '')::INTEGER
    ), 0) + 1, 6, '0')
    INTO v_seq
    FROM education.certificates
    WHERE certificate_number LIKE 'CERT-' || v_year || '-%';

    RETURN 'CERT-' || v_year || '-' || v_seq;
END;
$$ LANGUAGE plpgsql;

-- Funcion para generar codigo de verificacion
CREATE OR REPLACE FUNCTION education.generate_verification_code()
RETURNS TEXT AS $$
DECLARE
    v_chars TEXT := 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; -- Sin I, O, 0, 1 para evitar confusion
    v_code TEXT := '';
    i INTEGER;
BEGIN
    FOR i IN 1..8 LOOP
        v_code := v_code || SUBSTRING(v_chars FROM FLOOR(RANDOM() * LENGTH(v_chars) + 1)::INTEGER FOR 1);
    END LOOP;
    RETURN v_code;
END;
$$ LANGUAGE plpgsql;

-- Trigger para generar numeros automaticamente
CREATE OR REPLACE FUNCTION education.set_certificate_numbers()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.certificate_number IS NULL THEN
        NEW.certificate_number := education.generate_certificate_number();
    END IF;

    IF NEW.verification_code IS NULL THEN
        NEW.verification_code := education.generate_verification_code();
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS certificate_numbers ON education.certificates;
CREATE TRIGGER certificate_numbers
    BEFORE INSERT ON education.certificates
    FOR EACH ROW
    EXECUTE FUNCTION education.set_certificate_numbers();

-- Funcion para emitir certificado
CREATE OR REPLACE FUNCTION education.issue_certificate(
    p_enrollment_id UUID,
    p_type education.certificate_type DEFAULT 'completion'
)
RETURNS UUID AS $$
DECLARE
    v_enrollment RECORD;
    v_course RECORD;
    v_user RECORD;
    v_cert_id UUID;
BEGIN
    -- Obtener enrollment
    SELECT * INTO v_enrollment
    FROM education.enrollments
    WHERE id = p_enrollment_id;

    IF v_enrollment IS NULL THEN
        RAISE EXCEPTION 'Enrollment not found';
    END IF;

    -- Verificar que el curso este completado
    IF v_enrollment.status != 'completed' AND p_type = 'completion' THEN
        RAISE EXCEPTION 'Course not completed';
    END IF;

    -- Obtener curso
    SELECT * INTO v_course
    FROM education.courses
    WHERE id = v_enrollment.course_id;

    -- Obtener usuario
    SELECT * INTO v_user
    FROM users.users
    WHERE id = v_enrollment.user_id;

    -- Verificar si ya existe
    IF EXISTS (
        SELECT 1 FROM education.certificates
        WHERE enrollment_id = p_enrollment_id AND type = p_type
    ) THEN
        RAISE EXCEPTION 'Certificate already issued';
    END IF;

    -- Crear certificado
    INSERT INTO education.certificates (
        enrollment_id, course_id, user_id, tenant_id,
        type, title, recipient_name,
        final_score, grade, hours_completed
    ) VALUES (
        p_enrollment_id, v_enrollment.course_id, v_enrollment.user_id, v_enrollment.tenant_id,
        p_type,
        v_course.title || ' - Certificate of ' ||
            CASE p_type
                WHEN 'completion' THEN 'Completion'
                WHEN 'excellence' THEN 'Excellence'
                WHEN 'participation' THEN 'Participation'
                ELSE 'Achievement'
            END,
        COALESCE(v_user.first_name || ' ' || v_user.last_name, v_user.email),
        v_enrollment.final_score,
        v_enrollment.final_grade,
        v_course.estimated_duration_minutes / 60
    )
    RETURNING id INTO v_cert_id;

    -- Actualizar enrollment
    UPDATE education.enrollments
    SET certificate_issued = TRUE,
        certificate_id = v_cert_id,
        certificate_issued_at = NOW()
    WHERE id = p_enrollment_id;

    RETURN v_cert_id;
END;
$$ LANGUAGE plpgsql;

-- Funcion para verificar certificado
CREATE OR REPLACE FUNCTION education.verify_certificate(
    p_code VARCHAR(50)
)
RETURNS TABLE (
    is_valid BOOLEAN,
    certificate_number VARCHAR(50),
    recipient_name VARCHAR(200),
    course_title VARCHAR(200),
    completion_date DATE,
    grade VARCHAR(2),
    issued_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        c.is_valid AND (c.expires_at IS NULL OR c.expires_at > NOW()),
        c.certificate_number,
        c.recipient_name,
        co.title,
        c.completion_date,
        c.grade,
        c.issued_at,
        c.expires_at
    FROM education.certificates c
    JOIN education.courses co ON c.course_id = co.id
    WHERE c.certificate_number = p_code
       OR c.verification_code = p_code;
END;
$$ LANGUAGE plpgsql;

-- Vista de mis certificados
CREATE OR REPLACE VIEW education.v_my_certificates AS
SELECT
    c.id,
    c.certificate_number,
    c.course_id,
    co.title AS course_title,
    co.thumbnail_url AS course_thumbnail,
    c.type,
    c.title AS certificate_title,
    c.recipient_name,
    c.final_score,
    c.grade,
    c.completion_date,
    c.pdf_url,
    c.image_url,
    c.verification_code,
    c.is_public,
    c.public_url,
    c.expires_at,
    c.is_valid,
    c.issued_at
FROM education.certificates c
JOIN education.courses co ON c.course_id = co.id
WHERE c.is_valid = TRUE
ORDER BY c.issued_at DESC;

-- Vista de certificados publicos de un curso
CREATE OR REPLACE VIEW education.v_course_certificates AS
SELECT
    c.id,
    c.course_id,
    c.recipient_name,
    c.type,
    c.grade,
    c.completion_date,
    c.issued_at
FROM education.certificates c
WHERE c.is_public = TRUE
  AND c.is_valid = TRUE
ORDER BY c.issued_at DESC;

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

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

CREATE POLICY certificates_user_isolation ON education.certificates
    FOR SELECT
    USING (user_id = current_setting('app.current_user_id', true)::UUID);

-- Policy especial para verificacion publica
CREATE POLICY certificates_public_verify ON education.certificates
    FOR SELECT
    USING (is_public = TRUE OR is_valid = TRUE);

-- Grants
GRANT SELECT, INSERT, UPDATE ON education.certificates TO trading_app;
GRANT SELECT ON education.certificates TO trading_readonly;
GRANT SELECT ON education.v_my_certificates TO trading_app;
GRANT SELECT ON education.v_course_certificates TO trading_app;
GRANT EXECUTE ON FUNCTION education.issue_certificate TO trading_app;
GRANT EXECUTE ON FUNCTION education.verify_certificate TO trading_app;
GRANT EXECUTE ON FUNCTION education.generate_certificate_number TO trading_app;
GRANT EXECUTE ON FUNCTION education.generate_verification_code TO trading_app;
