// content/sub2api-panel.js — 页内脚本：SUB2API 后台（OAuth 生成与回调提交）

console.log('[MultiPage:sub2api-panel] Content script loaded on', location.href);

const SUB2API_PANEL_LISTENER_SENTINEL = 'data-multipage-sub2api-panel-listener';
const SUB2API_DEFAULT_GROUP_NAME = 'codex';
const SUB2API_DEFAULT_PROXY_NAME = '';
const SUB2API_DEFAULT_REDIRECT_URI = 'http://localhost:1455/auth/callback';
const SUB2API_DEFAULT_CONCURRENCY = 10;
const SUB2API_DEFAULT_PRIORITY = 1;
const SUB2API_DEFAULT_RATE_MULTIPLIER = 1;

if (document.documentElement.getAttribute(SUB2API_PANEL_LISTENER_SENTINEL) !== '1') {
  document.documentElement.setAttribute(SUB2API_PANEL_LISTENER_SENTINEL, '1');

  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.type === 'EXECUTE_STEP' || message.type === 'REQUEST_OAUTH_URL') {
      resetStopState();
      const handler = message.type === 'REQUEST_OAUTH_URL'
        ? requestOAuthUrl(message.payload)
        : handleStep(message.step, message.payload);
      handler.then((result) => {
        sendResponse({ ok: true, ...(result || {}) });
      }).catch((err) => {
        if (isStopError(err)) {
          if (message.step) {
            log('已被用户停止。', 'warn', { step: message.step });
          }
          sendResponse({ stopped: true, error: err.message });
          return;
        }
        if (message.step) {
          reportError(message.step, err.message);
        }
        sendResponse({ error: err.message });
      });
      return true;
    }
  });
} else {
  console.log('[MultiPage:sub2api-panel] 消息监听已存在，跳过重复注册');
}

function getSub2ApiOrigin(payload = {}) {
  const rawUrl = payload.sub2apiUrl || location.href;
  try {
    return new URL(rawUrl).origin;
  } catch {
    return location.origin;
  }
}

function normalizeRedirectUri() {
  const input = SUB2API_DEFAULT_REDIRECT_URI;
  const withProtocol = /^https?:\/\//i.test(input) ? input : `http://${input}`;
  const parsed = new URL(withProtocol);
  if (!parsed.pathname || parsed.pathname === '/') {
    parsed.pathname = '/auth/callback';
  }
  if (parsed.pathname !== '/auth/callback') {
    throw new Error('SUB2API 回调地址必须是 /auth/callback，例如 http://localhost:1455/auth/callback');
  }
  return parsed.toString();
}

async function handleStep(step, payload = {}) {
  switch (step) {
    case 1:
      return step1_generateOpenAiAuthUrl(payload);
    case 10:
    case 12:
    case 13:
      return step9_submitOpenAiCallback({ ...(payload || {}), visibleStep: step });
    default:
      throw new Error(`sub2api-panel.js 不处理步骤 ${step}`);
  }
}

async function requestOAuthUrl(payload = {}) {
  return step1_generateOpenAiAuthUrl(payload, { report: false });
}

async function requestJson(origin, path, options = {}) {
  throwIfStopped();
  const {
    method = 'GET',
    token = '',
    body = undefined,
  } = options;

  const response = await fetch(`${origin}${path}`, {
    method,
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json',
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
    },
    body: body === undefined ? undefined : JSON.stringify(body),
  });

  const text = await response.text();
  let json = null;
  try {
    json = text ? JSON.parse(text) : null;
  } catch {
    json = null;
  }

  if (json && typeof json === 'object' && 'code' in json) {
    if (json.code === 0) {
      return json.data;
    }
    throw new Error(json.message || json.detail || `请求失败（${path}）`);
  }

  if (!response.ok) {
    throw new Error((json && (json.message || json.detail)) || `请求失败（HTTP ${response.status}）：${path}`);
  }

  return json;
}

function storeAuthSession(loginData) {
  if (!loginData?.access_token) {
    throw new Error('SUB2API 登录返回缺少 access_token。');
  }

  localStorage.setItem('auth_token', loginData.access_token);
  if (loginData.refresh_token) {
    localStorage.setItem('refresh_token', loginData.refresh_token);
  } else {
    localStorage.removeItem('refresh_token');
  }
  if (loginData.expires_in) {
    localStorage.setItem('token_expires_at', String(Date.now() + Number(loginData.expires_in) * 1000));
  }
  if (loginData.user) {
    localStorage.setItem('auth_user', JSON.stringify(loginData.user));
  }
  sessionStorage.removeItem('auth_expired');
}

async function loginSub2Api(payload = {}) {
  const email = (payload.sub2apiEmail || '').trim();
  const password = payload.sub2apiPassword || '';
  const origin = getSub2ApiOrigin(payload);

  if (!email) {
    throw new Error('缺少 SUB2API 登录邮箱，请先在侧边栏填写。');
  }
  if (!password) {
    throw new Error('缺少 SUB2API 登录密码，请先在侧边栏填写。');
  }

  log('步骤：正在登录 SUB2API 后台...');
  const loginData = await requestJson(origin, '/api/v1/auth/login', {
    method: 'POST',
    body: {
      email,
      password,
    },
  });
  storeAuthSession(loginData);

  return {
    origin,
    token: loginData.access_token,
    user: loginData.user || null,
  };
}

async function getGroupByName(origin, token, groupName) {
  const targetName = (groupName || SUB2API_DEFAULT_GROUP_NAME).trim() || SUB2API_DEFAULT_GROUP_NAME;
  const groups = await requestJson(origin, '/api/v1/admin/groups/all', {
    method: 'GET',
    token,
  });

  const normalized = targetName.toLowerCase();
  const group = (groups || []).find((item) => {
    const itemName = String(item?.name || '').trim().toLowerCase();
    if (!itemName) return false;
    if (itemName !== normalized) return false;
    return !item.platform || item.platform === 'openai';
  });

  if (!group) {
    throw new Error(`SUB2API 中未找到名为“${targetName}”的 openai 分组。`);
  }

  return group;
}

function normalizeSub2ApiGroupNames(value) {
  const source = Array.isArray(value)
    ? value
    : String(value || '').split(/[\r\n,，;；]+/);
  const seen = new Set();
  const names = [];
  for (const item of source) {
    const name = String(item || '').trim();
    const key = name.toLowerCase();
    if (!name || seen.has(key)) continue;
    seen.add(key);
    names.push(name);
  }
  return names.length ? names : [SUB2API_DEFAULT_GROUP_NAME];
}

async function getGroupsByNames(origin, token, groupNames) {
  const targetNames = normalizeSub2ApiGroupNames(groupNames);
  const groups = await requestJson(origin, '/api/v1/admin/groups/all', {
    method: 'GET',
    token,
  });
  const matched = [];
  const missing = [];

  for (const targetName of targetNames) {
    const normalized = targetName.toLowerCase();
    const group = (groups || []).find((item) => {
      const itemName = String(item?.name || '').trim().toLowerCase();
      if (!itemName) return false;
      if (itemName !== normalized) return false;
      return !item.platform || item.platform === 'openai';
    });
    if (group) {
      matched.push(group);
    } else {
      missing.push(targetName);
    }
  }

  if (missing.length) {
    throw new Error(`SUB2API 中未找到以下 openai 分组：${missing.join('、')}。`);
  }

  return matched;
}

function normalizeSub2ApiProxyPreference(value) {
  return String(value || '').trim();
}

function resolveSub2ApiProxyPreference(payload = {}, backgroundState = {}) {
  if (payload.sub2apiDefaultProxyName !== undefined) {
    return normalizeSub2ApiProxyPreference(payload.sub2apiDefaultProxyName);
  }
  if (backgroundState.sub2apiDefaultProxyName !== undefined) {
    return normalizeSub2ApiProxyPreference(backgroundState.sub2apiDefaultProxyName);
  }
  return SUB2API_DEFAULT_PROXY_NAME;
}

function resolveSub2ApiAccountPriority(payload = {}, backgroundState = {}) {
  const candidate = payload.sub2apiAccountPriority !== undefined
    ? payload.sub2apiAccountPriority
    : backgroundState.sub2apiAccountPriority;
  const rawValue = String(candidate ?? '').trim();
  if (!rawValue) {
    return SUB2API_DEFAULT_PRIORITY;
  }
  const numeric = Number(rawValue);
  if (!Number.isSafeInteger(numeric) || numeric < 1) {
    throw new Error('SUB2API 账号优先级必须是大于等于 1 的整数。');
  }
  return numeric;
}

function normalizeProxyId(value) {
  if (value === undefined || value === null || value === '') {
    return null;
  }
  const normalized = Number(value);
  if (!Number.isSafeInteger(normalized) || normalized <= 0) {
    return null;
  }
  return normalized;
}

function buildProxyDisplayName(proxy = {}) {
  const id = normalizeProxyId(proxy.id);
  const name = String(proxy.name || '').trim();
  const protocol = String(proxy.protocol || '').trim();
  const host = String(proxy.host || '').trim();
  const port = proxy.port === undefined || proxy.port === null ? '' : String(proxy.port).trim();
  const address = protocol && host && port ? `${protocol}://${host}:${port}` : '';
  const parts = [
    name || '(未命名代理)',
    id ? `#${id}` : '',
    address,
  ].filter(Boolean);
  return parts.join(' ');
}

function buildProxySearchText(proxy = {}) {
  return [
    proxy.id,
    proxy.name,
    proxy.protocol,
    proxy.host,
    proxy.port,
    buildProxyDisplayName(proxy),
  ]
    .filter((value) => value !== undefined && value !== null && value !== '')
    .map((value) => String(value).trim().toLowerCase())
    .filter(Boolean)
    .join(' ');
}

function isActiveProxy(proxy = {}) {
  const status = String(proxy.status || '').trim().toLowerCase();
  return !status || status === 'active';
}

function findSub2ApiProxy(proxies = [], preference = '') {
  const activeProxies = (Array.isArray(proxies) ? proxies : [])
    .filter(isActiveProxy)
    .filter((proxy) => normalizeProxyId(proxy.id));
  const normalizedPreference = normalizeSub2ApiProxyPreference(preference).toLowerCase();
  const preferredId = normalizeProxyId(normalizedPreference);

  if (preferredId) {
    const matchedById = activeProxies.find((proxy) => normalizeProxyId(proxy.id) === preferredId);
    return {
      proxy: matchedById || null,
      reason: matchedById ? 'id' : 'missing-id',
      candidates: activeProxies,
    };
  }

  if (normalizedPreference) {
    const exactMatches = activeProxies.filter((proxy) => {
      const name = String(proxy.name || '').trim().toLowerCase();
      return name === normalizedPreference;
    });
    if (exactMatches.length === 1) {
      return { proxy: exactMatches[0], reason: 'name', candidates: activeProxies };
    }
    if (exactMatches.length > 1) {
      return { proxy: null, reason: 'ambiguous-name', candidates: exactMatches };
    }

    const fuzzyMatches = activeProxies.filter((proxy) => buildProxySearchText(proxy).includes(normalizedPreference));
    if (fuzzyMatches.length === 1) {
      return { proxy: fuzzyMatches[0], reason: 'fuzzy', candidates: activeProxies };
    }
    if (fuzzyMatches.length > 1) {
      return { proxy: null, reason: 'ambiguous-fuzzy', candidates: fuzzyMatches };
    }

    return { proxy: null, reason: 'missing-name', candidates: activeProxies };
  }

  if (activeProxies.length === 1) {
    return { proxy: activeProxies[0], reason: 'single-active', candidates: activeProxies };
  }
  return {
    proxy: null,
    reason: activeProxies.length ? 'no-preference' : 'none-active',
    candidates: activeProxies,
  };
}

async function resolveSub2ApiProxy(origin, token, preference = '') {
  const proxies = await requestJson(origin, '/api/v1/admin/proxies/all?with_count=true', {
    method: 'GET',
    token,
  });
  if (!Array.isArray(proxies)) {
    throw new Error('SUB2API 代理列表返回格式异常，无法自动选择代理。');
  }

  const { proxy, reason, candidates } = findSub2ApiProxy(proxies, preference);
  if (proxy) {
    return proxy;
  }

  const configured = normalizeSub2ApiProxyPreference(preference) || '(未配置)';
  const available = (candidates || [])
    .slice(0, 8)
    .map(buildProxyDisplayName)
    .join('，') || '无可用代理';
  if (reason === 'ambiguous-name' || reason === 'ambiguous-fuzzy') {
    throw new Error(`SUB2API 默认代理“${configured}”匹配到多个代理，请改填代理 ID。候选：${available}`);
  }
  if (reason === 'missing-id') {
    throw new Error(`SUB2API 默认代理 ID “${configured}”不存在或未启用。可用代理：${available}`);
  }
  if (reason === 'missing-name') {
    throw new Error(`SUB2API 默认代理“${configured}”不存在或未启用。可用代理：${available}`);
  }
  if (reason === 'no-preference') {
    throw new Error(`SUB2API 存在多个可用代理，请在侧边栏填写默认代理名称或 ID；留空则不使用代理。可用代理：${available}`);
  }
  throw new Error('SUB2API 没有可用代理；请检查默认代理配置，或将其留空以禁用代理。');
}

function buildDraftAccountName(groupName) {
  const prefix = (groupName || SUB2API_DEFAULT_GROUP_NAME)
    .trim()
    .replace(/[^\w\u4e00-\u9fa5-]+/g, '-')
    .replace(/^-+|-+$/g, '') || SUB2API_DEFAULT_GROUP_NAME;
  const stamp = new Date().toISOString().replace(/\D/g, '').slice(2, 14);
  const random = Math.floor(Math.random() * 9000 + 1000);
  return `${prefix}-${stamp}-${random}`;
}

function extractStateFromAuthUrl(authUrl) {
  try {
    return new URL(authUrl).searchParams.get('state') || '';
  } catch {
    return '';
  }
}

function parseLocalhostCallback(rawUrl, visibleStep = 10) {
  let parsed;
  try {
    parsed = new URL(rawUrl);
  } catch {
    throw new Error('提供的回调 URL 不是合法链接。');
  }

  if (!['http:', 'https:'].includes(parsed.protocol)) {
    throw new Error('回调 URL 协议不正确。');
  }
  if (!['localhost', '127.0.0.1'].includes(parsed.hostname)) {
    throw new Error(`步骤 ${visibleStep} 只接受 localhost / 127.0.0.1 回调地址。`);
  }
  if (parsed.pathname !== '/auth/callback') {
    throw new Error('回调 URL 路径必须是 /auth/callback。');
  }

  const code = (parsed.searchParams.get('code') || '').trim();
  const state = (parsed.searchParams.get('state') || '').trim();
  if (!code || !state) {
    throw new Error('回调 URL 中缺少 code 或 state。');
  }

  return {
    url: parsed.toString(),
    code,
    state,
  };
}

function buildOpenAiCredentials(exchangeData) {
  const credentials = {};
  const allowedKeys = [
    'access_token',
    'refresh_token',
    'id_token',
    'expires_at',
    'email',
    'chatgpt_account_id',
    'chatgpt_user_id',
    'organization_id',
    'plan_type',
    'client_id',
  ];

  for (const key of allowedKeys) {
    if (exchangeData?.[key] !== undefined && exchangeData?.[key] !== null && exchangeData?.[key] !== '') {
      credentials[key] = exchangeData[key];
    }
  }

  if (!credentials.access_token) {
    throw new Error('SUB2API 交换授权码后未返回 access_token。');
  }

  return credentials;
}

function buildOpenAiExtra(exchangeData) {
  const extra = {};
  const allowedKeys = ['email', 'name', 'privacy_mode'];

  for (const key of allowedKeys) {
    if (exchangeData?.[key] !== undefined && exchangeData?.[key] !== null && exchangeData?.[key] !== '') {
      extra[key] = exchangeData[key];
    }
  }

  return Object.keys(extra).length ? extra : undefined;
}

async function getBackgroundState() {
  try {
    return await chrome.runtime.sendMessage({ type: 'GET_STATE', source: 'sub2api-panel' });
  } catch {
    return {};
  }
}

function openAccountsPageSoon(origin) {
  const accountsUrl = `${origin}/admin/accounts`;
  if (location.href === accountsUrl || location.pathname.startsWith('/admin/accounts')) {
    return;
  }
  setTimeout(() => {
    try {
      location.replace(accountsUrl);
    } catch { }
  }, 500);
}

async function step1_generateOpenAiAuthUrl(payload = {}, options = {}) {
  const { report = true } = options;
  const logStep = Number.isInteger(payload?.logStep) ? payload.logStep : 1;
  const redirectUri = normalizeRedirectUri();
  const groupNames = normalizeSub2ApiGroupNames(payload.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME);
  const groupName = groupNames[0] || SUB2API_DEFAULT_GROUP_NAME;

  const { origin, token } = await loginSub2Api(payload);
  const groups = await getGroupsByNames(origin, token, groupNames);
  const group = groups[0];
  const proxyPreference = resolveSub2ApiProxyPreference(payload);
  const proxy = proxyPreference ? await resolveSub2ApiProxy(origin, token, proxyPreference) : null;
  const proxyId = normalizeProxyId(proxy?.id);
  const draftName = buildDraftAccountName(group.name || groupName);
  const groupLabel = groups.map((item) => `${item.name}（#${item.id}）`).join('、');

  log(`步骤 ${logStep}：已登录 SUB2API，使用分组 ${groupLabel}。`);
  if (proxy) {
    log(`步骤 ${logStep}：已选择 SUB2API 默认代理 ${buildProxyDisplayName(proxy)}。`);
  } else {
    log(`步骤 ${logStep}：未配置 SUB2API 默认代理，本次将不使用代理。`);
  }
  log(`步骤 ${logStep}：正在向 SUB2API 生成 OpenAI Auth 链接，回调地址为 ${redirectUri}。`);

  const authRequestBody = {
    redirect_uri: redirectUri,
  };
  if (proxyId) {
    authRequestBody.proxy_id = proxyId;
  }

  const authData = await requestJson(origin, '/api/v1/admin/openai/generate-auth-url', {
    method: 'POST',
    token,
    body: authRequestBody,
  });

  const oauthUrl = String(authData?.auth_url || '').trim();
  const sessionId = String(authData?.session_id || '').trim();
  const oauthState = String(authData?.state || extractStateFromAuthUrl(oauthUrl)).trim();

  if (!oauthUrl || !sessionId) {
    throw new Error('SUB2API 未返回完整的 auth_url / session_id。');
  }

  log(`步骤 ${logStep}：已获取 SUB2API OAuth 链接：${oauthUrl.slice(0, 96)}...`, 'ok');
  const result = {
    oauthUrl,
    sub2apiSessionId: sessionId,
    sub2apiOAuthState: oauthState,
    sub2apiGroupId: group.id,
    sub2apiGroupIds: groups.map((item) => item.id),
    sub2apiDraftName: draftName,
    sub2apiProxyId: proxyId,
  };
  if (report) {
    reportComplete(1, result);
  }
  openAccountsPageSoon(origin);
  return result;
}

async function step9_submitOpenAiCallback(payload = {}) {
  const visibleStep = Number(payload?.visibleStep) || 10;
  const callback = parseLocalhostCallback(payload.localhostUrl || '', visibleStep);
  const backgroundState = await getBackgroundState();
  const flowEmail = String(backgroundState.email || '').trim();

  const sessionId = String(payload.sub2apiSessionId || backgroundState.sub2apiSessionId || '').trim();
  const expectedState = String(payload.sub2apiOAuthState || backgroundState.sub2apiOAuthState || '').trim();

  const { origin, token } = await loginSub2Api(payload);
  const proxyPreference = resolveSub2ApiProxyPreference(payload, backgroundState);
  const preferredProxyId = normalizeProxyId(payload.sub2apiProxyId || backgroundState.sub2apiProxyId);
  const proxySelector = preferredProxyId || proxyPreference;
  const proxy = proxySelector ? await resolveSub2ApiProxy(origin, token, proxySelector) : null;
  const proxyId = normalizeProxyId(proxy?.id);
  const accountPriority = resolveSub2ApiAccountPriority(payload, backgroundState);
  const storedGroupIds = Array.isArray(payload.sub2apiGroupIds)
    ? payload.sub2apiGroupIds
    : (Array.isArray(backgroundState.sub2apiGroupIds) ? backgroundState.sub2apiGroupIds : []);
  const groupIdsFromState = storedGroupIds
    .map((id) => Number(id))
    .filter((id) => Number.isFinite(id) && id > 0);
  const groups = groupIdsFromState.length
    ? groupIdsFromState.map((id) => ({ id }))
    : (payload.sub2apiGroupId
      ? [{ id: payload.sub2apiGroupId, name: payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME }]
      : await getGroupsByNames(origin, token, payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME));

  if (!sessionId) {
    throw new Error('缺少 SUB2API session_id，请重新执行步骤 1。');
  }
  if (expectedState && expectedState !== callback.state) {
    throw new Error('本次 localhost 回调中的 state 与步骤 1 生成的 state 不一致，请重新执行步骤 1。');
  }

  log('正在向 SUB2API 交换 OpenAI 授权码...', 'info', { step: visibleStep, stepKey: 'platform-verify' });
  if (proxy) {
    log(`使用 SUB2API 默认代理 ${buildProxyDisplayName(proxy)}。`, 'info', { step: visibleStep, stepKey: 'platform-verify' });
  } else {
    log('未配置 SUB2API 默认代理，本次将不使用代理。', 'info', { step: visibleStep, stepKey: 'platform-verify' });
  }
  const exchangeRequestBody = {
    session_id: sessionId,
    code: callback.code,
    state: callback.state,
  };
  if (proxyId) {
    exchangeRequestBody.proxy_id = proxyId;
  }
  const exchangeData = await requestJson(origin, '/api/v1/admin/openai/exchange-code', {
    method: 'POST',
    token,
    body: exchangeRequestBody,
  });

  const credentials = buildOpenAiCredentials(exchangeData);
  const extra = buildOpenAiExtra(exchangeData);
  const resolvedEmail = String(exchangeData?.email || credentials?.email || '').trim();
  const groupIds = groups
    .map((group) => Number(group.id))
    .filter((id) => Number.isFinite(id) && id > 0);
  if (!groupIds.length) {
    throw new Error('SUB2API 返回的目标分组 ID 无效。');
  }
  const accountName = resolvedEmail
    || flowEmail
    || String(payload.sub2apiDraftName || backgroundState.sub2apiDraftName || '').trim()
    || buildDraftAccountName(payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME);
  const createPayload = {
    name: accountName,
    notes: '',
    platform: 'openai',
    type: 'oauth',
    credentials,
    concurrency: SUB2API_DEFAULT_CONCURRENCY,
    priority: accountPriority,
    rate_multiplier: SUB2API_DEFAULT_RATE_MULTIPLIER,
    group_ids: groupIds,
    auto_pause_on_expired: true,
  };
  if (proxyId) {
    createPayload.proxy_id = proxyId;
  }

  if (extra) {
    createPayload.extra = extra;
  }

  log(`授权码交换成功，正在创建 SUB2API 账号（名称：${accountName}）...`, 'info', { step: visibleStep, stepKey: 'platform-verify' });
  const createdAccount = await requestJson(origin, '/api/v1/admin/accounts', {
    method: 'POST',
    token,
    body: createPayload,
  });

  const verifiedStatus = `SUB2API 已创建账号 #${createdAccount?.id || 'unknown'}`;
  log(verifiedStatus, 'ok', { step: visibleStep, stepKey: 'platform-verify' });
  reportComplete(visibleStep, {
    localhostUrl: callback.url,
    verifiedStatus,
  });
  openAccountsPageSoon(origin);
}

reportReady();
