/* FinMind Provider — 對應系統文件 §6.1 Market Data Provider 介面
 *
 * window.FinMind = {
 *   getCandles(symbol, { from, to, timeframe }) → Promise<Candle[]>
 *   getToken() / setToken(token)
 *   clearCache()
 * }
 *
 * Candle 格式（與 chart.jsx genCandles 相容）：
 *   { date: Date, o: number, h: number, l: number, c: number, v: number }
 *
 * 設計：
 * - sessionStorage 快取，TTL 預設 1 小時，重整不重抓
 * - Token 走 localStorage，可在 Settings 頁設定
 * - 同一 cacheKey 的並發請求只發一次（in-flight dedupe）
 * - 錯誤往上拋，呼叫端自行決定是否 fallback
 */

const FINMIND_BASE = "https://api.finmindtrade.com/api/v4/data";
const CACHE_PREFIX = "twstock.finmind.";
const TOKEN_KEY = "finmind.token";
const DEFAULT_TTL_MS = 1000 * 60 * 60; // 1h

const inflight = new Map();

function getToken() {
  return (window.TWStorage && window.TWStorage.get(TOKEN_KEY)) || "";
}

function setToken(token) {
  if (window.TWStorage) window.TWStorage.set(TOKEN_KEY, token || "");
}

function readCache(key, ttl) {
  try {
    const raw = sessionStorage.getItem(CACHE_PREFIX + key);
    if (!raw) return null;
    const { ts, data } = JSON.parse(raw);
    if (Date.now() - ts > ttl) return null;
    return data;
  } catch (e) {
    return null;
  }
}

function writeCache(key, data) {
  try {
    sessionStorage.setItem(CACHE_PREFIX + key, JSON.stringify({ ts: Date.now(), data }));
  } catch (e) {
    /* quota — 忽略 */
  }
}

function clearCache() {
  try {
    const keys = Object.keys(sessionStorage).filter((k) => k.startsWith(CACHE_PREFIX));
    keys.forEach((k) => sessionStorage.removeItem(k));
  } catch (e) {}
}

function isUS(symbol) {
  return !symbol.endsWith(".TW");
}

function rawId(symbol) {
  return symbol.replace(/\.TW$/, "");
}

function isoDate(d) {
  if (typeof d === "string") return d;
  return new Date(d).toISOString().slice(0, 10);
}

/* 統一 TW / US 兩種欄位命名 */
function normalizeRow(row, us) {
  const o = us ? row.Open : row.open;
  const h = us ? row.High : row.max;
  const l = us ? row.Low : row.min;
  const c = us ? row.Close : row.close;
  const v = us ? row.Volume : row.Trading_Volume;
  return {
    date: new Date(row.date),
    o: Number(o),
    h: Number(h),
    l: Number(l),
    c: Number(c),
    v: Number(v) || 0,
  };
}

async function fetchJson(url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`FinMind HTTP ${res.status}`);
  const json = await res.json();
  if (json.status !== 200) throw new Error(`FinMind ${json.status}: ${json.msg || "unknown"}`);
  return json;
}

/* 主 API：取日 K 線
 * symbol: "2330.TW" / "AAPL"
 * opts: { from, to, ttl }
 */
async function getCandles(symbol, opts = {}) {
  const us = isUS(symbol);
  const id = rawId(symbol);
  const today = new Date();
  const fiveYearsAgo = new Date(today.getTime() - 5 * 365 * 24 * 60 * 60 * 1000);
  const from = isoDate(opts.from || fiveYearsAgo);
  const to = isoDate(opts.to || today);
  const ttl = opts.ttl || DEFAULT_TTL_MS;

  const dataset = us ? "USStockPrice" : "TaiwanStockPrice";
  const cacheKey = `candles.${dataset}.${id}.${from}.${to}`;

  const cached = readCache(cacheKey, ttl);
  if (cached) {
    return cached.map((r) => ({ ...r, date: new Date(r.date) }));
  }

  if (inflight.has(cacheKey)) return inflight.get(cacheKey);

  const params = new URLSearchParams({
    dataset,
    data_id: id,
    start_date: from,
    end_date: to,
  });
  const token = getToken();
  if (token) params.set("token", token);

  const promise = fetchJson(`${FINMIND_BASE}?${params}`)
    .then((json) => {
      const candles = (json.data || [])
        .map((r) => normalizeRow(r, us))
        .filter((c) => Number.isFinite(c.c) && Number.isFinite(c.h) && Number.isFinite(c.l));
      writeCache(cacheKey, candles);
      return candles;
    })
    .finally(() => {
      inflight.delete(cacheKey);
    });

  inflight.set(cacheKey, promise);
  return promise;
}

/* 全台股代號 / 名稱對應表（搜尋用）— localStorage 快取 24h
 * 回傳 [{ sym: "2330.TW", name: "台積電", market: "TW" }, ...] */
const TW_UNIVERSE_KEY = "twstock.tw-universe";
const TW_UNIVERSE_TTL = 24 * 60 * 60 * 1000;
let twUniverseInflight = null;

async function getTaiwanUniverse() {
  try {
    const raw = localStorage.getItem(TW_UNIVERSE_KEY);
    if (raw) {
      const { ts, data } = JSON.parse(raw);
      if (Date.now() - ts < TW_UNIVERSE_TTL && Array.isArray(data) && data.length) return data;
    }
  } catch (e) {}
  if (twUniverseInflight) return twUniverseInflight;

  const params = new URLSearchParams({ dataset: "TaiwanStockInfo" });
  const token = getToken();
  if (token) params.set("token", token);

  twUniverseInflight = fetchJson(`${FINMIND_BASE}?${params}`)
    .then((json) => {
      const seen = new Set();
      const list = [];
      for (const r of json.data || []) {
        if (!r.stock_id || seen.has(r.stock_id)) continue;
        if (!/^\d+$/.test(r.stock_id)) continue; /* 排除權證/ETF 衍生：保留純數字 4-6 碼 */
        seen.add(r.stock_id);
        list.push({ sym: `${r.stock_id}.TW`, name: r.stock_name || r.stock_id, market: "TW" });
      }
      try { localStorage.setItem(TW_UNIVERSE_KEY, JSON.stringify({ ts: Date.now(), data: list })); } catch (e) {}
      return list;
    })
    .finally(() => { twUniverseInflight = null; });
  return twUniverseInflight;
}

window.FinMind = { getCandles, getToken, setToken, clearCache, getTaiwanUniverse };
