-- ===========================================
-- MECANICAS DIESEL - Firma Electronica Basica
-- ===========================================
-- Resuelve: GAP-12
-- Sistema de firma canvas para aprobacion de cotizaciones
-- Nota: Para NOM-151 completa ver SPEC-FIRMA-ELECTRONICA-NOM151.md

-- ============================================
-- EXTENSION DE QUOTES PARA FIRMA
-- ============================================

-- Agregar campos de firma a cotizaciones
DO $$
BEGIN
    -- signature_data (canvas base64)
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'signature_data'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN signature_data TEXT;
        COMMENT ON COLUMN service_management.quotes.signature_data IS 'Firma canvas en formato base64 PNG';
    END IF;

    -- signed_at
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'signed_at'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN signed_at TIMESTAMPTZ;
        COMMENT ON COLUMN service_management.quotes.signed_at IS 'Fecha y hora de firma';
    END IF;

    -- signed_by_name
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'signed_by_name'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN signed_by_name VARCHAR(256);
        COMMENT ON COLUMN service_management.quotes.signed_by_name IS 'Nombre de quien firmo';
    END IF;

    -- signed_by_ip
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'signed_by_ip'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN signed_by_ip VARCHAR(45);
        COMMENT ON COLUMN service_management.quotes.signed_by_ip IS 'IP desde donde se firmo';
    END IF;

    -- signed_by_email
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'signed_by_email'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN signed_by_email VARCHAR(256);
        COMMENT ON COLUMN service_management.quotes.signed_by_email IS 'Email de quien firmo';
    END IF;

    -- signature_hash
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'signature_hash'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN signature_hash VARCHAR(128);
        COMMENT ON COLUMN service_management.quotes.signature_hash IS 'Hash SHA-256 del documento al momento de firmar';
    END IF;

    -- approval_token
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'approval_token'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN approval_token VARCHAR(64);
        COMMENT ON COLUMN service_management.quotes.approval_token IS 'Token unico para link de aprobacion';
    END IF;

    -- token_expires_at
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'service_management'
          AND table_name = 'quotes'
          AND column_name = 'token_expires_at'
    ) THEN
        ALTER TABLE service_management.quotes
        ADD COLUMN token_expires_at TIMESTAMPTZ;
        COMMENT ON COLUMN service_management.quotes.token_expires_at IS 'Expiracion del token de aprobacion';
    END IF;
END $$;

-- Indice para busqueda por token
CREATE INDEX IF NOT EXISTS idx_quotes_approval_token
ON service_management.quotes(approval_token)
WHERE approval_token IS NOT NULL;

-- ============================================
-- TABLA DE HISTORIAL DE FIRMAS
-- ============================================

-- Historial de todas las firmas (para auditoria)
CREATE TABLE IF NOT EXISTS service_management.signature_audit (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Documento firmado
    document_type VARCHAR(50) NOT NULL,        -- 'quote', 'service_order', etc.
    document_id UUID NOT NULL,
    document_number VARCHAR(50),

    -- Datos del firmante
    signer_name VARCHAR(256) NOT NULL,
    signer_email VARCHAR(256),
    signer_phone VARCHAR(50),
    signer_ip VARCHAR(45),
    signer_user_agent TEXT,

    -- Firma
    signature_data TEXT NOT NULL,              -- Base64 de la imagen de firma
    signature_method VARCHAR(50) NOT NULL DEFAULT 'canvas',  -- canvas, typed, upload

    -- Integridad
    document_hash VARCHAR(128) NOT NULL,       -- Hash del documento al firmar
    signature_hash VARCHAR(128),               -- Hash de la firma
    document_snapshot JSONB,                   -- Snapshot del documento

    -- Contexto
    action VARCHAR(50) NOT NULL,               -- 'approve', 'reject', 'acknowledge'
    comments TEXT,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Geolocation (opcional, si el cliente lo permite)
    geo_latitude DECIMAL(10, 8),
    geo_longitude DECIMAL(11, 8),
    geo_accuracy DECIMAL(10, 2)
);

COMMENT ON TABLE service_management.signature_audit IS 'Registro de auditoria de todas las firmas electronicas';

-- Indices
CREATE INDEX idx_sig_audit_tenant ON service_management.signature_audit(tenant_id);
CREATE INDEX idx_sig_audit_document ON service_management.signature_audit(document_type, document_id);
CREATE INDEX idx_sig_audit_signer ON service_management.signature_audit(signer_email);
CREATE INDEX idx_sig_audit_created ON service_management.signature_audit(created_at DESC);

-- RLS
SELECT create_tenant_rls_policies('service_management', 'signature_audit');

-- ============================================
-- FUNCIONES AUXILIARES
-- ============================================

-- Funcion para generar token de aprobacion
CREATE OR REPLACE FUNCTION service_management.generate_approval_token(
    p_quote_id UUID,
    p_expires_hours INTEGER DEFAULT 72
)
RETURNS VARCHAR(64) AS $$
DECLARE
    v_token VARCHAR(64);
BEGIN
    -- Generar token aleatorio
    v_token := encode(gen_random_bytes(32), 'hex');

    -- Actualizar cotizacion con token
    UPDATE service_management.quotes
    SET
        approval_token = v_token,
        token_expires_at = NOW() + (p_expires_hours || ' hours')::INTERVAL
    WHERE id = p_quote_id;

    RETURN v_token;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION service_management.generate_approval_token IS 'Genera token de aprobacion para cotizacion';

-- Funcion para validar token
CREATE OR REPLACE FUNCTION service_management.validate_approval_token(
    p_token VARCHAR(64)
)
RETURNS TABLE (
    quote_id UUID,
    is_valid BOOLEAN,
    error_message TEXT
) AS $$
DECLARE
    v_quote RECORD;
BEGIN
    -- Buscar cotizacion con token
    SELECT q.id, q.status, q.token_expires_at, q.signed_at
    INTO v_quote
    FROM service_management.quotes q
    WHERE q.approval_token = p_token;

    -- Token no encontrado
    IF v_quote.id IS NULL THEN
        RETURN QUERY SELECT
            NULL::UUID,
            false,
            'Token invalido o no encontrado'::TEXT;
        RETURN;
    END IF;

    -- Token expirado
    IF v_quote.token_expires_at < NOW() THEN
        RETURN QUERY SELECT
            v_quote.id,
            false,
            'El token ha expirado'::TEXT;
        RETURN;
    END IF;

    -- Ya firmada
    IF v_quote.signed_at IS NOT NULL THEN
        RETURN QUERY SELECT
            v_quote.id,
            false,
            'La cotizacion ya fue firmada'::TEXT;
        RETURN;
    END IF;

    -- Cotizacion no esta en estado correcto
    IF v_quote.status NOT IN ('sent', 'pending') THEN
        RETURN QUERY SELECT
            v_quote.id,
            false,
            'La cotizacion no esta disponible para aprobacion'::TEXT;
        RETURN;
    END IF;

    -- Token valido
    RETURN QUERY SELECT
        v_quote.id,
        true,
        NULL::TEXT;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION service_management.validate_approval_token IS 'Valida token de aprobacion y estado de cotizacion';

-- Funcion para firmar cotizacion
CREATE OR REPLACE FUNCTION service_management.sign_quote(
    p_quote_id UUID,
    p_signature_data TEXT,
    p_signer_name VARCHAR(256),
    p_signer_email VARCHAR(256) DEFAULT NULL,
    p_signer_ip VARCHAR(45) DEFAULT NULL,
    p_user_agent TEXT DEFAULT NULL,
    p_comments TEXT DEFAULT NULL,
    p_action VARCHAR(50) DEFAULT 'approve'
)
RETURNS UUID AS $$
DECLARE
    v_quote RECORD;
    v_audit_id UUID;
    v_document_hash VARCHAR(128);
    v_signature_hash VARCHAR(128);
    v_new_status VARCHAR(20);
BEGIN
    -- Obtener cotizacion
    SELECT q.*, c.name as customer_name
    INTO v_quote
    FROM service_management.quotes q
    LEFT JOIN workshop_core.customers c ON c.id = q.customer_id
    WHERE q.id = p_quote_id;

    IF v_quote.id IS NULL THEN
        RAISE EXCEPTION 'Quote % not found', p_quote_id;
    END IF;

    -- Verificar que no este ya firmada
    IF v_quote.signed_at IS NOT NULL THEN
        RAISE EXCEPTION 'Quote already signed on %', v_quote.signed_at;
    END IF;

    -- Generar hash del documento (simplificado - en produccion usar contenido completo)
    v_document_hash := encode(
        sha256(
            (v_quote.id::TEXT || v_quote.total::TEXT || v_quote.created_at::TEXT)::bytea
        ),
        'hex'
    );

    -- Generar hash de la firma
    v_signature_hash := encode(sha256(p_signature_data::bytea), 'hex');

    -- Determinar nuevo estado
    v_new_status := CASE p_action
        WHEN 'approve' THEN 'approved'
        WHEN 'reject' THEN 'rejected'
        ELSE 'pending'
    END;

    -- Actualizar cotizacion
    UPDATE service_management.quotes
    SET
        status = v_new_status,
        signature_data = p_signature_data,
        signed_at = NOW(),
        signed_by_name = p_signer_name,
        signed_by_email = p_signer_email,
        signed_by_ip = p_signer_ip,
        signature_hash = v_document_hash,
        approval_token = NULL,  -- Invalidar token
        token_expires_at = NULL,
        updated_at = NOW()
    WHERE id = p_quote_id;

    -- Crear registro de auditoria
    INSERT INTO service_management.signature_audit (
        tenant_id,
        document_type, document_id, document_number,
        signer_name, signer_email, signer_ip, signer_user_agent,
        signature_data, signature_method,
        document_hash, signature_hash,
        document_snapshot,
        action, comments
    )
    VALUES (
        v_quote.tenant_id,
        'quote', p_quote_id, v_quote.quote_number,
        p_signer_name, p_signer_email, p_signer_ip, p_user_agent,
        p_signature_data, 'canvas',
        v_document_hash, v_signature_hash,
        jsonb_build_object(
            'quote_number', v_quote.quote_number,
            'customer_name', v_quote.customer_name,
            'total', v_quote.total,
            'created_at', v_quote.created_at,
            'items_count', (SELECT COUNT(*) FROM service_management.quote_lines WHERE quote_id = p_quote_id)
        ),
        p_action, p_comments
    )
    RETURNING id INTO v_audit_id;

    RETURN v_audit_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION service_management.sign_quote IS 'Firma una cotizacion con firma canvas y crea registro de auditoria';

-- ============================================
-- VISTAS
-- ============================================

-- Vista de cotizaciones pendientes de firma
CREATE VIEW service_management.v_quotes_pending_signature AS
SELECT
    q.id,
    q.tenant_id,
    q.quote_number,
    q.status,
    q.total,
    c.name as customer_name,
    c.email as customer_email,
    c.phone as customer_phone,
    q.approval_token IS NOT NULL as has_token,
    q.token_expires_at,
    CASE
        WHEN q.token_expires_at IS NULL THEN 'no_token'
        WHEN q.token_expires_at < NOW() THEN 'expired'
        WHEN q.token_expires_at < NOW() + INTERVAL '24 hours' THEN 'expiring_soon'
        ELSE 'valid'
    END as token_status,
    q.created_at,
    q.updated_at
FROM service_management.quotes q
LEFT JOIN workshop_core.customers c ON c.id = q.customer_id
WHERE q.status IN ('sent', 'pending')
  AND q.signed_at IS NULL
ORDER BY q.created_at DESC;

COMMENT ON VIEW service_management.v_quotes_pending_signature IS 'Cotizaciones pendientes de firma del cliente';

-- Vista de historial de firmas
CREATE VIEW service_management.v_signature_history AS
SELECT
    sa.id,
    sa.tenant_id,
    sa.document_type,
    sa.document_number,
    sa.signer_name,
    sa.signer_email,
    sa.action,
    sa.created_at as signed_at,
    sa.signer_ip,
    sa.comments,
    -- Info adicional del documento
    CASE sa.document_type
        WHEN 'quote' THEN (SELECT total FROM service_management.quotes WHERE id = sa.document_id)
        ELSE NULL
    END as document_total
FROM service_management.signature_audit sa
ORDER BY sa.created_at DESC;

COMMENT ON VIEW service_management.v_signature_history IS 'Historial de firmas electronicas';

-- ============================================
-- GRANTS
-- ============================================
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA service_management TO mecanicas_user;
GRANT SELECT ON service_management.signature_audit TO mecanicas_user;
GRANT INSERT ON service_management.signature_audit TO mecanicas_user;
