-- ============================================================================
-- FINANCIAL SCHEMA - Tabla: payment_methods
-- ============================================================================
-- Metodos de pago guardados por usuarios para pagos recurrentes
-- Integra con Stripe para almacenamiento seguro de tarjetas y cuentas
-- ============================================================================

-- Enum para tipo de metodo de pago guardado
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'saved_payment_type' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'financial')) THEN
        CREATE TYPE financial.saved_payment_type AS ENUM (
            'card',
            'bank_account',
            'sepa_debit',
            'crypto_wallet'
        );
    END IF;
END$$;

-- Enum para estado del metodo de pago
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'payment_method_status' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'financial')) THEN
        CREATE TYPE financial.payment_method_status AS ENUM (
            'pending_verification',
            'active',
            'expired',
            'failed',
            'removed'
        );
    END IF;
END$$;

CREATE TABLE IF NOT EXISTS financial.payment_methods (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    customer_id UUID REFERENCES financial.customers(id) ON DELETE SET NULL,

    -- Stripe integration
    stripe_payment_method_id VARCHAR(100) UNIQUE,
    stripe_fingerprint VARCHAR(100), -- Para detectar duplicados

    -- Tipo y estado
    payment_type financial.saved_payment_type NOT NULL,
    status financial.payment_method_status NOT NULL DEFAULT 'pending_verification',

    -- Informacion del metodo (datos no sensibles)
    -- Para tarjetas: last4, brand, exp_month, exp_year
    -- Para bancos: last4, bank_name, account_type
    display_info JSONB NOT NULL DEFAULT '{}',

    -- Metodo por defecto
    is_default BOOLEAN NOT NULL DEFAULT FALSE,

    -- Datos de tarjeta (solo informacion visible)
    card_brand VARCHAR(20), -- 'visa', 'mastercard', 'amex', etc.
    card_last4 VARCHAR(4),
    card_exp_month INTEGER,
    card_exp_year INTEGER,
    card_funding VARCHAR(20), -- 'credit', 'debit', 'prepaid'

    -- Datos de cuenta bancaria (solo informacion visible)
    bank_name VARCHAR(100),
    bank_last4 VARCHAR(4),
    bank_account_type VARCHAR(20), -- 'checking', 'savings'

    -- Datos de crypto wallet
    crypto_network VARCHAR(20), -- 'ethereum', 'bitcoin', 'polygon'
    crypto_address_last8 VARCHAR(8),

    -- Verificacion
    verified_at TIMESTAMPTZ,
    verification_method VARCHAR(50), -- 'micro_deposits', '3d_secure', 'instant'

    -- Billing address (para 3DS y validacion)
    billing_address JSONB DEFAULT '{}',

    -- Metadata
    metadata JSONB DEFAULT '{}',

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ, -- Para tarjetas con fecha de expiracion
    removed_at TIMESTAMPTZ,

    -- Constraints
    CONSTRAINT chk_card_info CHECK (
        payment_type != 'card' OR (
            card_brand IS NOT NULL AND
            card_last4 IS NOT NULL AND
            card_exp_month IS NOT NULL AND
            card_exp_year IS NOT NULL
        )
    ),
    CONSTRAINT chk_bank_info CHECK (
        payment_type != 'bank_account' OR (
            bank_name IS NOT NULL AND
            bank_last4 IS NOT NULL
        )
    ),
    CONSTRAINT chk_crypto_info CHECK (
        payment_type != 'crypto_wallet' OR (
            crypto_network IS NOT NULL AND
            crypto_address_last8 IS NOT NULL
        )
    ),
    CONSTRAINT chk_valid_exp_month CHECK (
        card_exp_month IS NULL OR (card_exp_month >= 1 AND card_exp_month <= 12)
    ),
    CONSTRAINT chk_valid_exp_year CHECK (
        card_exp_year IS NULL OR card_exp_year >= 2024
    )
);

-- Indices
CREATE INDEX idx_payment_methods_user ON financial.payment_methods(user_id);
CREATE INDEX idx_payment_methods_customer ON financial.payment_methods(customer_id);
CREATE INDEX idx_payment_methods_stripe ON financial.payment_methods(stripe_payment_method_id);
CREATE INDEX idx_payment_methods_status ON financial.payment_methods(status);
CREATE INDEX idx_payment_methods_default ON financial.payment_methods(user_id, is_default)
    WHERE is_default = TRUE;
CREATE INDEX idx_payment_methods_fingerprint ON financial.payment_methods(stripe_fingerprint)
    WHERE stripe_fingerprint IS NOT NULL;
CREATE INDEX idx_payment_methods_active ON financial.payment_methods(user_id, payment_type)
    WHERE status = 'active';

-- Comentarios
COMMENT ON TABLE financial.payment_methods IS 'Metodos de pago guardados por usuarios con integracion Stripe';
COMMENT ON COLUMN financial.payment_methods.stripe_payment_method_id IS 'ID del PaymentMethod en Stripe';
COMMENT ON COLUMN financial.payment_methods.stripe_fingerprint IS 'Fingerprint para detectar tarjetas duplicadas';
COMMENT ON COLUMN financial.payment_methods.display_info IS 'Informacion visible del metodo para UI';
COMMENT ON COLUMN financial.payment_methods.is_default IS 'Metodo de pago por defecto del usuario';
COMMENT ON COLUMN financial.payment_methods.billing_address IS 'Direccion de facturacion para 3D Secure';

-- Trigger para asegurar un solo metodo por defecto por usuario
CREATE OR REPLACE FUNCTION financial.ensure_single_default_payment_method()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.is_default = TRUE THEN
        UPDATE financial.payment_methods
        SET is_default = FALSE, updated_at = NOW()
        WHERE user_id = NEW.user_id
          AND id != NEW.id
          AND is_default = TRUE;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_ensure_single_default_payment_method
    BEFORE INSERT OR UPDATE OF is_default ON financial.payment_methods
    FOR EACH ROW
    WHEN (NEW.is_default = TRUE)
    EXECUTE FUNCTION financial.ensure_single_default_payment_method();

-- Funcion para marcar tarjetas expiradas
CREATE OR REPLACE FUNCTION financial.check_expired_cards()
RETURNS INTEGER AS $$
DECLARE
    v_count INTEGER;
BEGIN
    UPDATE financial.payment_methods
    SET status = 'expired', updated_at = NOW()
    WHERE payment_type = 'card'
      AND status = 'active'
      AND (
          card_exp_year < EXTRACT(YEAR FROM CURRENT_DATE) OR
          (card_exp_year = EXTRACT(YEAR FROM CURRENT_DATE) AND card_exp_month < EXTRACT(MONTH FROM CURRENT_DATE))
      );

    GET DIAGNOSTICS v_count = ROW_COUNT;
    RETURN v_count;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION financial.check_expired_cards() IS 'Marca como expiradas las tarjetas vencidas. Ejecutar mensualmente.';
