import {configured,db} from './server';
import {getWallexMarkets} from './wallex-server';
import {wallexFresh} from './wallex';
import {combineExchangeRates,defaultExchangePolicy,exchangeUnavailable,parseExchangePolicy,tomanRateToRial,type ExchangePolicy,type ExchangeRate,type ExchangeSample} from './exchange-rate';

/** Settings are non-secret site configuration, preserved by the admin content editor. */
export async function readExchangePolicy():Promise<ExchangePolicy>{
 if(!configured())return {...defaultExchangePolicy,sources:[...defaultExchangePolicy.sources]};
 const response=await db('site_content?key=eq.site&select=data');
 if(!response.ok)throw new Error('exchange_configuration_unavailable');
 const rows=await response.json() as {data?:{exchange_rate?:unknown}}[];
 if(!Array.isArray(rows))throw new Error('exchange_configuration_unavailable');
 const value=rows[0]?.data?.exchange_rate;
 return value===undefined?{...defaultExchangePolicy,sources:[...defaultExchangePolicy.sources]}:parseExchangePolicy(value);
}

export function normalizeNobitexRate(value:unknown,receivedAt:string):ExchangeSample{
 const data=value as {status?:string;stats?:Record<string,{latest?:unknown;isClosed?:unknown}>};
 const market=data?.stats?.['usdt-rls'];
 if(data?.status!=='ok'||market?.isClosed!==false||typeof market.latest!=='string'||!/^\d{1,15}(?:\.\d{1,12})?$/.test(market.latest)||Number(market.latest)<=0)throw new Error('invalid_exchange_quote');
 return {source:'nobitex',rateRial:market.latest,receivedAt};
}
let nobitexCache:ExchangeSample|null=null,nobitexPending:Promise<ExchangeSample>|null=null,nobitexRetryAt=0;
async function nobitexRate():Promise<ExchangeSample>{
 if(nobitexCache&&Date.now()-Date.parse(nobitexCache.receivedAt)<20000)return nobitexCache;
 if(nobitexPending)return nobitexPending;
 if(Date.now()<nobitexRetryAt)throw new Error('exchange_unavailable');
 nobitexPending=(async()=>{
  try{
   const response=await fetch('https://apiv2.nobitex.ir/market/stats?srcCurrency=usdt&dstCurrency=rls',{headers:{Accept:'application/json'},cache:'no-store',redirect:'manual',signal:AbortSignal.timeout(8000)});
   if(!response.ok)throw new Error('exchange_unavailable');
   nobitexCache=normalizeNobitexRate(await response.json(),new Date().toISOString());return nobitexCache;
  }catch{nobitexRetryAt=Date.now()+10000;throw new Error('exchange_unavailable')}finally{nobitexPending=null}
 })();return nobitexPending;
}
async function wallexRate():Promise<ExchangeSample>{
 const feed=await getWallexMarkets(),quote=feed.quotes.find(q=>q.symbol==='USDTTMN');
 if(!wallexFresh(quote))throw new Error('exchange_unavailable');
 const rial=tomanRateToRial(quote.price);if(rial===null)throw new Error('exchange_unavailable');
 return {source:'wallex',rateRial:rial,receivedAt:quote.receivedAt};
}
// Registry keeps provider URLs and credentials out of public/configurable input.
const providers={wallex:wallexRate,nobitex:nobitexRate};
let pending:Promise<ExchangeRate>|null=null;
export function getExchangeRate():Promise<ExchangeRate>{
 if(pending)return pending;
 pending=(async()=>{
  try{
   const policy=await readExchangePolicy();
   const samples=await Promise.all(policy.sources.map(source=>providers[source]()));
   return combineExchangeRates(policy,samples);
  }catch{return exchangeUnavailable()}finally{pending=null}
 })();return pending;
}
