// ═══════════════════════════════════════════════════════════
//  companyintel.js — 公司研究資料：管理層、內部人交易、新聞、產業鏈入口
// ═══════════════════════════════════════════════════════════
const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36';
const SEC_UA = 'EmmyInvestDashboard/1.0 (personal learning tool; contact@example.com)';

async function text(url, headers = {}, ms = 12000) {
  const ctrl = new AbortController();
  const timer = setTimeout(() => ctrl.abort(), ms);
  try {
    const res = await fetch(url, { headers: { 'User-Agent': UA, ...headers }, signal: ctrl.signal });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.text();
  } finally { clearTimeout(timer); }
}
async function json(url, headers = {}, ms = 12000) {
  return JSON.parse(await text(url, { Accept: 'application/json,text/plain,*/*', ...headers }, ms));
}
const strip = (s) => String(s || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
const num = (s) => {
  if (s == null) return null;
  const n = Number(String(s).replace(/[$,%\s,]/g, ''));
  return Number.isFinite(n) ? n : null;
};
const tag = (src, name) => src.match(new RegExp(`<${name}>([\\s\\S]*?)<\\/${name}>`, 'i'))?.[1]?.trim() || null;

let _tickerMap = null;
async function tickerToCik(symbol) {
  if (!_tickerMap) {
    const d = await json('https://www.sec.gov/files/company_tickers.json', { 'User-Agent': SEC_UA });
    _tickerMap = {};
    for (const k of Object.keys(d)) _tickerMap[String(d[k].ticker).toUpperCase()] = { cik: String(d[k].cik_str).padStart(10, '0'), name: d[k].title };
  }
  return _tickerMap[symbol] || null;
}

let _auth = { cookie: null, crumb: null, at: 0 };
async function yahooAuth() {
  if (_auth.crumb && Date.now() - _auth.at < 3600e3) return _auth;
  const r1 = await fetch('https://fc.yahoo.com/', { headers: { 'User-Agent': UA } }).catch(() => null);
  const cookie = (r1 && (r1.headers.get('set-cookie') || '')).split(';')[0] || '';
  const r2 = await fetch('https://query2.finance.yahoo.com/v1/test/getcrumb', { headers: { 'User-Agent': UA, Cookie: cookie } });
  const crumb = (await r2.text()).trim();
  if (!crumb || crumb.includes('<')) throw new Error('無法取得 Yahoo crumb');
  _auth = { cookie, crumb, at: Date.now() };
  return _auth;
}

async function fetchManagement(symbol) {
  try {
    const { cookie, crumb } = await yahooAuth();
    const d = await json(`https://query2.finance.yahoo.com/v10/finance/quoteSummary/${encodeURIComponent(symbol)}?modules=assetProfile&crumb=${encodeURIComponent(crumb)}`, { Cookie: cookie });
    const p = d?.quoteSummary?.result?.[0]?.assetProfile || {};
    return {
      sector: p.sector || null,
      industry: p.industry || null,
      website: p.website || null,
      fullTimeEmployees: p.fullTimeEmployees ?? null,
      officers: (p.companyOfficers || []).slice(0, 10).map(o => ({
        name: o.name || '',
        title: o.title || '',
        age: o.age ?? null,
        fiscalYear: o.fiscalYear ?? null,
        totalPay: o.totalPay?.raw ?? null,
      })),
      source: 'Yahoo assetProfile',
    };
  } catch {
    return { officers: [], source: null };
  }
}

function parseForm4(txt, filing) {
  const xml = txt.slice(txt.indexOf('<ownershipDocument'));
  const ownerBlock = xml.match(/<reportingOwner>([\s\S]*?)<\/reportingOwner>/i)?.[1] || '';
  const issuerBlock = xml.match(/<issuer>([\s\S]*?)<\/issuer>/i)?.[1] || '';
  const relBlock = ownerBlock.match(/<reportingOwnerRelationship>([\s\S]*?)<\/reportingOwnerRelationship>/i)?.[1] || '';
  const txBlocks = [...xml.matchAll(/<nonDerivativeTransaction>([\s\S]*?)<\/nonDerivativeTransaction>/gi)].map(m => m[1]);
  const transactions = txBlocks.slice(0, 8).map(b => ({
    date: tag(b, 'transactionDate') ? tag(tag(b, 'transactionDate'), 'value') : null,
    code: tag(b, 'transactionCode') || null,
    acquiredDisposed: tag(tag(b, 'transactionAcquiredDisposedCode') || '', 'value'),
    shares: num(tag(tag(b, 'transactionShares') || '', 'value')),
    price: num(tag(tag(b, 'transactionPricePerShare') || '', 'value')),
    ownedAfter: num(tag(tag(b, 'sharesOwnedFollowingTransaction') || '', 'value')),
  })).filter(t => t.shares != null || t.code);
  const acquired = transactions.filter(t => t.acquiredDisposed === 'A').reduce((a, t) => a + (t.shares || 0), 0);
  const disposed = transactions.filter(t => t.acquiredDisposed === 'D').reduce((a, t) => a + (t.shares || 0), 0);
  return {
    filingDate: filing.date,
    reportDate: tag(xml, 'periodOfReport'),
    owner: tag(ownerBlock, 'rptOwnerName') || strip(txt.match(/COMPANY CONFORMED NAME:\s*([^\n]+)/)?.[1]),
    issuer: tag(issuerBlock, 'issuerName'),
    title: tag(relBlock, 'officerTitle') || (tag(relBlock, 'isDirector') === '1' ? 'Director' : ''),
    isDirector: tag(relBlock, 'isDirector') === '1',
    isOfficer: tag(relBlock, 'isOfficer') === '1',
    acquired, disposed,
    signal: acquired > disposed ? 'acquire' : disposed > acquired ? 'dispose' : 'mixed',
    transactions,
    url: filing.url,
  };
}
async function fetchInsiderTransactions(symbol) {
  const hit = await tickerToCik(symbol);
  if (!hit) return [];
  const sub = await json(`https://data.sec.gov/submissions/CIK${hit.cik}.json`, { 'User-Agent': SEC_UA });
  const f = sub.filings?.recent || {};
  const filings = [];
  for (let i = 0; i < (f.form || []).length && filings.length < 8; i++) {
    if (f.form[i] !== '4') continue;
    const accn = f.accessionNumber[i];
    const accNo = accn.replace(/-/g, '');
    filings.push({
      date: f.filingDate[i],
      accn,
      url: `https://www.sec.gov/Archives/edgar/data/${Number(hit.cik)}/${accNo}/${accn}.txt`,
    });
  }
  const out = [];
  for (const filing of filings.slice(0, 5)) {
    try { out.push(parseForm4(await text(filing.url, { 'User-Agent': SEC_UA }), filing)); }
    catch { /* keep going */ }
  }
  return out;
}

async function fetchNews(symbol) {
  const d = await json(`https://api.nasdaq.com/api/news/topic/articlebysymbol?q=${encodeURIComponent(symbol)}|stocks&offset=0&limit=8&fallback=true`, {
    Accept: 'application/json', Origin: 'https://www.nasdaq.com', Referer: 'https://www.nasdaq.com/',
  }).catch(() => null);
  return (d?.data?.rows || []).slice(0, 8).map(r => ({
    title: r.title,
    publisher: r.publisher,
    created: r.created || r.ago,
    description: strip(r.description || ''),
    url: r.url ? (r.url.startsWith('http') ? r.url : `https://www.nasdaq.com${r.url}`) : null,
    relatedSymbols: (r.related_symbols || []).map(x => String(x).split('|')[0].toUpperCase()).filter(Boolean),
  }));
}

function industryChain(symbol, profile = {}) {
  const industry = `${profile.industry || ''} ${profile.sector || ''}`.toLowerCase();
  const maps = [
    {
      match: /semiconductor|chip|accelerated|technology/,
      upstream: ['EDA/IP 軟體', '晶圓代工', '先進封裝', 'HBM/記憶體', '半導體設備', 'ABF/載板'],
      peers: ['AMD', 'AVGO', 'QCOM', 'MRVL', 'TSM', 'ASML', 'MU'],
      downstream: ['雲端資料中心', 'AI 伺服器 OEM/ODM', '企業 AI 軟體', '自駕車/機器人', '遊戲與工作站'],
    },
    {
      match: /software|internet|communication|media/,
      upstream: ['雲端基礎設施', '資料中心', '廣告技術', '內容/資料供應商'],
      peers: ['MSFT', 'GOOGL', 'META', 'AMZN', 'CRM', 'ORCL'],
      downstream: ['企業客戶', '消費者流量', '開發者生態', '廣告主'],
    },
    {
      match: /consumer|retail|apparel/,
      upstream: ['原物料', '製造代工', '物流倉儲', '通路平台'],
      peers: ['AMZN', 'WMT', 'COST', 'TGT', 'NKE'],
      downstream: ['消費者', '會員訂閱', '門市/電商通路'],
    },
  ];
  const hit = maps.find(m => m.match.test(industry)) || {
    upstream: ['原物料/零組件', '設備與服務供應商', '物流與通路', '資本支出供應商'],
    peers: [],
    downstream: ['終端客戶', '企業採購', '通路夥伴', '替代產品'],
  };
  const q = encodeURIComponent(`${symbol} suppliers customers upstream downstream competitors`);
  return {
    upstream: hit.upstream,
    peers: hit.peers.filter(s => s !== symbol),
    downstream: hit.downstream,
    searches: [
      { label: '供應商 / 客戶', url: `https://www.google.com/search?q=${q}` },
      { label: '10-K supply chain', url: `https://www.google.com/search?q=${encodeURIComponent(`${symbol} 10-K suppliers customers supply chain`)}` },
      { label: '同業比較', url: `https://www.google.com/search?q=${encodeURIComponent(`${symbol} competitors industry peers`)}` },
    ],
  };
}

export async function getCompanyIntel(symbol, profile = {}) {
  symbol = String(symbol || '').trim().toUpperCase();
  const [management, insiders, news] = await Promise.all([
    fetchManagement(symbol),
    fetchInsiderTransactions(symbol).catch(() => []),
    fetchNews(symbol).catch(() => []),
  ]);
  return {
    symbol,
    updatedAt: new Date().toISOString(),
    management: {
      ...management,
      searches: [
        { label: '管理層 / Leadership', url: `https://www.google.com/search?q=${encodeURIComponent(`${symbol} executive officers management leadership`)}` },
        { label: 'Proxy / DEF 14A', url: `https://www.google.com/search?q=${encodeURIComponent(`${symbol} DEF 14A executive compensation board directors`)}` },
        { label: 'Investor relations', url: `https://www.google.com/search?q=${encodeURIComponent(`${symbol} investor relations leadership`)}` },
      ],
    },
    insiders,
    news,
    industryChain: industryChain(symbol, { ...profile, ...management }),
    sources: ['Yahoo assetProfile', 'SEC Form 4', 'Nasdaq News'],
  };
}
