-- =====================================================
-- ORBIQUANT IA - CURRENCY EXCHANGE RATES TABLE
-- =====================================================
-- Description: Historical exchange rates for multi-currency support
-- Schema: financial
-- =====================================================

CREATE TABLE financial.currency_exchange_rates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  -- Par de monedas
  from_currency financial.currency_code NOT NULL,
  to_currency financial.currency_code NOT NULL,

  -- Tasa de cambio
  rate DECIMAL(18,8) NOT NULL,

  -- Fuente de datos
  source VARCHAR(100) NOT NULL DEFAULT 'manual', -- manual, api, stripe, coinbase, etc.
  provider VARCHAR(100), -- nombre del proveedor si es API

  -- Validez temporal
  valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  valid_to TIMESTAMPTZ,

  -- Metadata
  metadata JSONB DEFAULT '{}',

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

  -- Constraints
  CONSTRAINT positive_rate CHECK (rate > 0),
  CONSTRAINT different_currencies CHECK (from_currency != to_currency),
  CONSTRAINT valid_dates_order CHECK (
    valid_to IS NULL OR valid_to > valid_from
  ),
  CONSTRAINT unique_rate_period UNIQUE(from_currency, to_currency, valid_from)
);

-- Indexes
CREATE INDEX idx_cer_currencies ON financial.currency_exchange_rates(from_currency, to_currency);
CREATE INDEX idx_cer_valid_from ON financial.currency_exchange_rates(valid_from DESC);
-- Index for currently valid rates (without time-based predicate for immutability)
CREATE INDEX idx_cer_valid_period ON financial.currency_exchange_rates(from_currency, to_currency, valid_from DESC)
  WHERE valid_to IS NULL;
CREATE INDEX idx_cer_source ON financial.currency_exchange_rates(source);

-- Comments
COMMENT ON TABLE financial.currency_exchange_rates IS 'Historical exchange rates for currency conversion';
COMMENT ON COLUMN financial.currency_exchange_rates.rate IS 'Exchange rate: 1 from_currency = rate * to_currency';
COMMENT ON COLUMN financial.currency_exchange_rates.source IS 'Source of exchange rate data';
COMMENT ON COLUMN financial.currency_exchange_rates.valid_from IS 'Start of rate validity period';
COMMENT ON COLUMN financial.currency_exchange_rates.valid_to IS 'End of rate validity period (NULL = currently valid)';
COMMENT ON COLUMN financial.currency_exchange_rates.metadata IS 'Additional rate metadata (bid, ask, spread, etc.)';

-- Función helper para obtener tasa de cambio actual
CREATE OR REPLACE FUNCTION financial.get_exchange_rate(
  p_from_currency financial.currency_code,
  p_to_currency financial.currency_code,
  p_at_time TIMESTAMPTZ DEFAULT NOW()
)
RETURNS DECIMAL(18,8)
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
  v_rate DECIMAL(18,8);
BEGIN
  -- Si son la misma moneda, retornar 1
  IF p_from_currency = p_to_currency THEN
    RETURN 1.0;
  END IF;

  -- Buscar tasa de cambio válida
  SELECT rate INTO v_rate
  FROM financial.currency_exchange_rates
  WHERE from_currency = p_from_currency
    AND to_currency = p_to_currency
    AND valid_from <= p_at_time
    AND (valid_to IS NULL OR valid_to > p_at_time)
  ORDER BY valid_from DESC
  LIMIT 1;

  -- Si no se encuentra, intentar inversa
  IF v_rate IS NULL THEN
    SELECT 1.0 / rate INTO v_rate
    FROM financial.currency_exchange_rates
    WHERE from_currency = p_to_currency
      AND to_currency = p_from_currency
      AND valid_from <= p_at_time
      AND (valid_to IS NULL OR valid_to > p_at_time)
    ORDER BY valid_from DESC
    LIMIT 1;
  END IF;

  -- Si aún no hay tasa, retornar NULL
  RETURN v_rate;
END;
$$;

COMMENT ON FUNCTION financial.get_exchange_rate IS 'Get exchange rate between currencies at specific time';

-- Función para convertir montos
CREATE OR REPLACE FUNCTION financial.convert_currency(
  p_amount DECIMAL,
  p_from_currency financial.currency_code,
  p_to_currency financial.currency_code,
  p_at_time TIMESTAMPTZ DEFAULT NOW()
)
RETURNS DECIMAL(20,8)
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
  v_rate DECIMAL(18,8);
BEGIN
  -- Obtener tasa de cambio
  v_rate := financial.get_exchange_rate(p_from_currency, p_to_currency, p_at_time);

  -- Si no hay tasa, retornar NULL
  IF v_rate IS NULL THEN
    RETURN NULL;
  END IF;

  -- Convertir y retornar
  RETURN p_amount * v_rate;
END;
$$;

COMMENT ON FUNCTION financial.convert_currency IS 'Convert amount between currencies at specific time';
