import { NextResponse } from 'next/server';

import {
  pgFindDailyExchangeRate,
  pgGetDefaultCurrency,
  pgGetExchangeRate,
  pgUpsertDailyExchangeRate,
} from '@/lib/postgres/data-access';

const PROVIDER = 'https://api.exchangerate.host';
const FALLBACK_PROVIDER = 'https://api.frankfurter.app';

async function fetchRateFromExchangeRateHost(base: string, currency: string, date: string) {
  const url = `${PROVIDER}/${date}?base=${currency}&symbols=${base}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`exchange-rate-host ${res.status}`);
  const data = await res.json();
  const rate = Number(data?.rates?.[base]);
  if (!Number.isFinite(rate) || rate <= 0) throw new Error('exchange-rate-host invalid rate');
  return {
    rate,
    date: String(data?.date || date),
    source: PROVIDER,
  };
}

async function fetchRateFromFrankfurter(base: string, currency: string, date: string) {
  const url = `${FALLBACK_PROVIDER}/${date}?from=${currency}&to=${base}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`frankfurter ${res.status}`);
  const data = await res.json();
  const rate = Number(data?.rates?.[base]);
  if (!Number.isFinite(rate) || rate <= 0) throw new Error('frankfurter invalid rate');
  return {
    rate,
    date: String(data?.date || date),
    source: FALLBACK_PROVIDER,
  };
}

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const currency = (searchParams.get('currency') || searchParams.get('target') || searchParams.get('to') || '')
    .trim()
    .toUpperCase();
  const defaultCurrency = await pgGetDefaultCurrency();
  const defaultCurrencyCode =
    defaultCurrency && typeof defaultCurrency === 'object' && typeof (defaultCurrency as { code?: unknown }).code === 'string'
      ? (defaultCurrency as { code: string }).code
      : '';
  const base = (searchParams.get('base') || searchParams.get('from') || defaultCurrencyCode || '')
    .trim()
    .toUpperCase();
  const requestedSource = (searchParams.get('source') || '').trim();
  const forceRefresh = searchParams.get('force') === '1' || searchParams.get('force') === 'true';
  const dateParam = searchParams.get('date') || new Date().toISOString();
  const targetDate = new Date(dateParam);

  if (Number.isNaN(targetDate.getTime())) {
    return NextResponse.json({ error: 'Invalid date' }, { status: 400 });
  }

  if (!currency) {
    return NextResponse.json({ error: 'Missing currency' }, { status: 400 });
  }

  if (!base) {
    return NextResponse.json({ error: 'Missing base currency' }, { status: 400 });
  }

  if (base === currency) {
    const formattedDate = targetDate.toISOString().slice(0, 10);
    return NextResponse.json({
      rate: 1,
      date: formattedDate,
      baseCurrency: base,
      currency,
      source: 'identity',
      isAuto: true,
      cached: false,
    });
  }

  const cached = await pgFindDailyExchangeRate(base, currency, targetDate);
  if (cached && !forceRefresh) {
    return NextResponse.json({
      rate: cached.rate,
      date: cached.date,
      baseCurrency: cached.baseCurrency,
      currency: cached.currency,
      source: cached.source,
      isAuto: cached.isAuto ?? true,
      cached: true,
    });
  }

  try {
    const formattedDate = targetDate.toISOString().slice(0, 10);
    let resolved: { rate: number; date: string; source: string } | null = null;

    try {
      resolved = await fetchRateFromExchangeRateHost(base, currency, formattedDate);
    } catch {
      resolved = await fetchRateFromFrankfurter(base, currency, formattedDate);
    }

    const rate = Number(resolved?.rate || 0);
    const resolvedDate = String(resolved?.date || formattedDate);
    const resolvedSource = String(resolved?.source || PROVIDER);

    const stored = await pgUpsertDailyExchangeRate({
          baseCurrency: base,
          currency,
          rate,
          date: resolvedDate,
          source: requestedSource || resolvedSource,
          isAuto: true,
        });

    return NextResponse.json({
      rate: stored.rate,
      date: stored.date,
      baseCurrency: stored.baseCurrency,
      currency: stored.currency,
      source: stored.source,
      isAuto: true,
      cached: false,
    });
  } catch (error: any) {
    try {
      const fallbackRate = await pgGetExchangeRate(currency, base);
      if (Number.isFinite(fallbackRate) && fallbackRate > 0) {
        const formattedDate = targetDate.toISOString().slice(0, 10);
        const localSource = requestedSource || 'local-config';
        const stored = await pgUpsertDailyExchangeRate({
              baseCurrency: base,
              currency,
              rate: fallbackRate,
              date: formattedDate,
              source: localSource,
              isAuto: false,
            });

        return NextResponse.json({
          rate: stored.rate,
          date: stored.date,
          baseCurrency: stored.baseCurrency,
          currency: stored.currency,
          source: stored.source,
          isAuto: false,
          cached: false,
          warning: error?.message || 'Providers unavailable, local fallback used',
        });
      }
    } catch {
      // ignore and return provider error below
    }

    return NextResponse.json({ error: error?.message || 'Failed to fetch rate' }, { status: 500 });
  }
}
