/**
 * AI bookmark recommendations by topic/theme for the current folder.
 */

import { callHostedAiChat, PRO_FEATURES } from './membership.js';
import {
  loadAiOrganizeSettings,
  canRunAiOrganize,
  ensureAiHostPermission,
  isHostedAiProvider,
  resolveHostedModel
} from './ai-bookmark-organize.js';

const DEFAULT_COUNT = 8;
const MIN_COUNT = 3;
const MAX_COUNT = 16;

function i18n(key, fallback, substitutions) {
  const msg = chrome.i18n.getMessage(key, substitutions);
  return msg || fallback || key;
}

function normalizeBaseUrl(baseUrl) {
  return String(baseUrl || '').trim().replace(/\/+$/, '');
}

function buildAiRequestHeaders(settings) {
  const headers = {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${String(settings.apiKey || '').trim()}`
  };
  const baseUrl = normalizeBaseUrl(settings.baseUrl);
  if (settings.provider === 'openrouter' || baseUrl.includes('openrouter.ai')) {
    headers['HTTP-Referer'] = chrome.runtime.getURL('src/index.html');
    headers['X-Title'] = 'SuperMark';
  }
  return headers;
}

function extractJsonObject(text) {
  if (text && typeof text === 'object') return text;
  const trimmed = String(text || '').trim();
  if (!trimmed || trimmed === '[object Object]') {
    throw new Error('EMPTY_AI_RESPONSE');
  }
  const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
  const candidate = fenced ? fenced[1].trim() : trimmed;
  const start = candidate.indexOf('{');
  const end = candidate.lastIndexOf('}');
  if (start === -1 || end === -1 || end <= start) {
    throw new Error('INVALID_AI_JSON');
  }
  return JSON.parse(candidate.slice(start, end + 1));
}

function normalizeUrl(raw) {
  let url = String(raw || '').trim();
  if (!url) return '';
  if (!/^https?:\/\//i.test(url)) {
    url = `https://${url}`;
  }
  try {
    const parsed = new URL(url);
    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return '';
    return parsed.toString();
  } catch {
    return '';
  }
}

function buildRecommendPrompt(topic, count, localeHint) {
  return [
    `Recommend ${count} high-quality websites for the topic/theme below.`,
    `User locale: ${localeHint}. Prefer sites useful for that locale when relevant, but keep widely known good sites.`,
    'Return JSON only with this shape:',
    '{"sites":[{"title":"Site name","url":"https://...","reason":"one short sentence"}]}',
    'Rules:',
    `- Exactly ${count} items when possible (at least ${Math.min(count, MIN_COUNT)}).`,
    '- Real, currently useful websites (not made-up domains).',
    '- Prefer official / reputable sites.',
    '- Titles short; reasons under 80 characters.',
    '- No duplicates.',
    '',
    `Topic: ${topic}`
  ].join('\n');
}

function validateRecommendations(raw, requestedCount) {
  const list = Array.isArray(raw?.sites) ? raw.sites : Array.isArray(raw) ? raw : [];
  const seen = new Set();
  const sites = [];

  for (const item of list) {
    const title = String(item?.title || item?.name || '').trim().slice(0, 80);
    const url = normalizeUrl(item?.url || item?.link || '');
    const reason = String(item?.reason || item?.description || '').trim().slice(0, 120);
    if (!title || !url) continue;
    const key = url.replace(/\/+$/, '').toLowerCase();
    if (seen.has(key)) continue;
    seen.add(key);
    sites.push({ title, url, reason });
    if (sites.length >= Math.min(MAX_COUNT, Math.max(requestedCount, MIN_COUNT))) break;
  }

  if (sites.length < MIN_COUNT) {
    throw new Error('TOO_FEW_RECOMMENDATIONS');
  }
  return sites;
}

async function callHostedRecommend(topic, count, settings) {
  const localeHint = (chrome.i18n.getUILanguage() || 'en').replace('_', '-');
  const model = resolveHostedModel(settings);
  const data = await callHostedAiChat({
    temperature: 0.4,
    maxTokens: 1600,
    model,
    feature: PRO_FEATURES.AI_RECOMMEND,
    messages: [
      {
        role: 'system',
        content: 'You are a helpful web discovery assistant. Respond with JSON only.'
      },
      {
        role: 'user',
        content: buildRecommendPrompt(topic, count, localeHint)
      }
    ]
  });
  const content = data?.choices?.[0]?.message?.content;
  return validateRecommendations(extractJsonObject(content), count);
}

async function callByokRecommendOnce(topic, count, settings, { jsonMode }) {
  const localeHint = (chrome.i18n.getUILanguage() || 'en').replace('_', '-');
  const endpoint = `${normalizeBaseUrl(settings.baseUrl)}/chat/completions`;
  const body = {
    model: settings.model.trim(),
    temperature: 0.4,
    messages: [
      {
        role: 'system',
        content: 'You are a helpful web discovery assistant. Respond with JSON only.'
      },
      {
        role: 'user',
        content: buildRecommendPrompt(topic, count, localeHint)
      }
    ]
  };
  if (jsonMode) {
    body.response_format = { type: 'json_object' };
  }

  const response = await fetch(endpoint, {
    method: 'POST',
    headers: buildAiRequestHeaders(settings),
    body: JSON.stringify(body)
  });

  if (!response.ok) {
    let detail = '';
    try {
      const errBody = await response.json();
      detail = errBody?.error?.message || errBody?.message || '';
    } catch {
      detail = await response.text().catch(() => '');
    }
    const error = new Error(detail || `HTTP_${response.status}`);
    error.status = response.status;
    throw error;
  }

  const data = await response.json();
  const content = data?.choices?.[0]?.message?.content;
  return validateRecommendations(extractJsonObject(content), count);
}

async function callByokRecommend(topic, count, settings) {
  try {
    return await callByokRecommendOnce(topic, count, settings, { jsonMode: true });
  } catch (error) {
    if (
      String(error.message || '').toLowerCase().includes('response_format')
      || error.status === 400
    ) {
      return callByokRecommendOnce(topic, count, settings, { jsonMode: false });
    }
    throw error;
  }
}

export async function fetchAiBookmarkRecommendations(topic, count = DEFAULT_COUNT) {
  const cleanedTopic = String(topic || '').trim().slice(0, 120);
  if (cleanedTopic.length < 2) {
    throw new Error('TOPIC_REQUIRED');
  }

  const requested = Math.min(
    MAX_COUNT,
    Math.max(MIN_COUNT, parseInt(count, 10) || DEFAULT_COUNT)
  );

  const settings = await loadAiOrganizeSettings();
  const readiness = await canRunAiOrganize(settings, { aiFeature: PRO_FEATURES.AI_RECOMMEND });
  if (!readiness.ok) {
    throw new Error(readiness.reason || 'AI_NOT_CONFIGURED');
  }

  const useHosted = readiness.mode === 'hosted' || isHostedAiProvider(settings);

  if (useHosted) {
    try {
      const hostedSettings = await loadAiOrganizeSettings();
      return await callHostedRecommend(cleanedTopic, requested, hostedSettings);
    } catch (error) {
      const code = error.code || error.message;
      if (
        (code === 'quota_exceeded' || code === 'quota_none' || error.status === 429 || error.status === 403)
        && String(settings.apiKey || '').trim()
        && !isHostedAiProvider(settings)
      ) {
        // Fall through to BYOK
      } else if (code === 'quota_exceeded' || code === 'quota_none') {
        throw new Error(code === 'quota_none' ? 'AI_QUOTA_NONE' : 'AI_QUOTA_EXCEEDED');
      } else if (code === 'NOT_SIGNED_IN') {
        throw new Error('NOT_SIGNED_IN');
      } else if (code === 'invalid_model' || code === 'MODEL_REQUIRED') {
        throw new Error('AI_NOT_CONFIGURED');
      } else {
        throw error;
      }
    }
  }

  if (!String(settings.apiKey || '').trim() || isHostedAiProvider(settings)) {
    throw new Error('AI_NOT_CONFIGURED');
  }

  const granted = await ensureAiHostPermission(settings.baseUrl);
  if (!granted) throw new Error('PERMISSION_DENIED');
  return callByokRecommend(cleanedTopic, requested, settings);
}

async function createBookmark({ parentId, title, url }) {
  return new Promise((resolve, reject) => {
    chrome.bookmarks.create({ parentId, title, url }, (result) => {
      if (chrome.runtime.lastError) {
        reject(new Error(chrome.runtime.lastError.message));
        return;
      }
      resolve(result);
    });
  });
}

export async function addRecommendedBookmarks(folderId, sites) {
  let added = 0;
  for (const site of sites) {
    try {
      await createBookmark({
        parentId: folderId,
        title: site.title,
        url: site.url
      });
      added += 1;
    } catch (error) {
      console.warn('Failed to add recommended bookmark', site.url, error);
    }
  }
  return added;
}

export function getAiRecommendErrorMessage(error) {
  const code = String(error?.code || error?.message || error || '');
  if (code === 'TOPIC_REQUIRED') {
    return i18n('aiRecommendTopicRequired', 'Please enter a topic');
  }
  if (code === 'NOT_SIGNED_IN') {
    return i18n('aiOrganizeNeedSignIn', 'Sign in to use SuperMark hosted AI');
  }
  if (code === 'AI_QUOTA_EXCEEDED' || code === 'AI_QUOTA_NONE') {
    return i18n(
      'aiOrganizeQuotaExceeded',
      'Monthly hosted AI quota used up. Upgrade or use your own API key.'
    );
  }
  if (code === 'AI_NOT_CONFIGURED') {
    return i18n('aiOrganizeNeedConfig', 'Please configure AI API in Settings → AI first');
  }
  if (code === 'PERMISSION_DENIED') {
    return i18n('aiOrganizePermissionDenied', 'Network permission denied for the AI API');
  }
  if (code === 'TOO_FEW_RECOMMENDATIONS' || code === 'INVALID_AI_JSON' || code === 'EMPTY_AI_RESPONSE') {
    return i18n('aiRecommendInvalidResponse', 'AI returned an invalid recommendation list');
  }
  return i18n(
    'aiRecommendError',
    'AI recommend failed: $1',
    [code.slice(0, 120)]
  );
}

/**
 * Interactive recommend dialog: topic input → AI results → add selected.
 * @returns {Promise<{ added: number, sites: Array } | null>}
 */
export function showAiRecommendDialog(folderId) {
  return new Promise((resolve) => {
    const dialog = document.getElementById('ai-recommend-dialog');
    const topicInput = document.getElementById('ai-recommend-topic');
    const countInput = document.getElementById('ai-recommend-count');
    const generateBtn = document.getElementById('ai-recommend-generate-button');
    const statusEl = document.getElementById('ai-recommend-status');
    const inputPanel = document.getElementById('ai-recommend-input-panel');
    const resultsPanel = document.getElementById('ai-recommend-results-panel');
    const listEl = document.getElementById('ai-recommend-list');
    const summaryEl = document.getElementById('ai-recommend-summary');
    const addBtn = document.getElementById('ai-recommend-add-button');
    const backBtn = document.getElementById('ai-recommend-back-button');
    const cancelBtn = document.getElementById('ai-recommend-cancel-button');
    const closeBtn = document.getElementById('ai-recommend-close-button');

    if (!dialog || !topicInput || !generateBtn || !listEl) {
      const topic = window.prompt(
        i18n('aiRecommendTopicPrompt', 'What topic should AI recommend sites for?')
      );
      if (!topic) {
        resolve(null);
        return;
      }
      fetchAiBookmarkRecommendations(topic, DEFAULT_COUNT)
        .then(async (sites) => {
          const added = await addRecommendedBookmarks(folderId, sites);
          resolve({ added, sites });
        })
        .catch((error) => {
          console.error(error);
          resolve(null);
        });
      return;
    }

    const countPresets = dialog.querySelectorAll('.ai-recommend-count-preset');

    let currentSites = [];
    let busy = false;

    const setStatus = (text, isError = false) => {
      if (!statusEl) return;
      statusEl.hidden = !text;
      statusEl.textContent = text || '';
      statusEl.classList.toggle('is-error', Boolean(isError));
    };

    const showInput = () => {
      if (inputPanel) inputPanel.hidden = false;
      if (resultsPanel) resultsPanel.hidden = true;
      if (addBtn) addBtn.hidden = true;
      if (backBtn) backBtn.hidden = true;
      if (generateBtn) generateBtn.hidden = false;
      setStatus('');
    };

    const showResults = (sites, topic) => {
      currentSites = sites;
      if (inputPanel) inputPanel.hidden = true;
      if (resultsPanel) resultsPanel.hidden = false;
      if (generateBtn) generateBtn.hidden = true;
      if (addBtn) {
        addBtn.hidden = false;
        addBtn.disabled = false;
      }
      if (backBtn) backBtn.hidden = false;

      if (summaryEl) {
        summaryEl.textContent = i18n(
          'aiRecommendSummary',
          'Recommendations for “$1” — select sites to add',
          [topic]
        );
      }

      listEl.innerHTML = '';
      for (const site of sites) {
        const item = document.createElement('label');
        item.className = 'ai-recommend-item';

        const check = document.createElement('input');
        check.type = 'checkbox';
        check.className = 'ai-recommend-item-check';
        check.checked = true;
        check.dataset.url = site.url;

        const body = document.createElement('div');
        body.className = 'ai-recommend-item-body';

        const title = document.createElement('div');
        title.className = 'ai-recommend-item-title';
        title.textContent = site.title;

        const meta = document.createElement('div');
        meta.className = 'ai-recommend-item-meta';
        meta.textContent = site.url;

        body.appendChild(title);
        body.appendChild(meta);
        if (site.reason) {
          const reason = document.createElement('div');
          reason.className = 'ai-recommend-item-reason';
          reason.textContent = site.reason;
          body.appendChild(reason);
        }

        item.appendChild(check);
        item.appendChild(body);
        listEl.appendChild(item);
      }
      setStatus('');
    };

    const getSelectedSites = () => {
      const checked = new Set(
        [...listEl.querySelectorAll('.ai-recommend-item-check:checked')].map(
          (el) => el.dataset.url
        )
      );
      return currentSites.filter((site) => checked.has(site.url));
    };

    const cleanup = (result) => {
      generateBtn?.removeEventListener('click', onGenerate);
      addBtn?.removeEventListener('click', onAdd);
      backBtn?.removeEventListener('click', onBack);
      cancelBtn?.removeEventListener('click', onCancel);
      closeBtn?.removeEventListener('click', onCancel);
      topicInput?.removeEventListener('keydown', onTopicKeydown);
      countInput?.removeEventListener('input', onCountInput);
      countPresets.forEach((btn) => {
        btn.removeEventListener('click', onCountPresetClick);
      });
      dialog.style.display = 'none';
      resolve(result);
    };

    const onCancel = () => {
      if (busy) return;
      cleanup(null);
    };

    const onBack = () => {
      if (busy) return;
      showInput();
    };

    const onGenerate = async () => {
      if (busy) return;
      const topic = topicInput.value.trim();
      if (topic.length < 2) {
        setStatus(i18n('aiRecommendTopicRequired', 'Please enter a topic'), true);
        topicInput.focus();
        return;
      }

      busy = true;
      generateBtn.disabled = true;
      topicInput.disabled = true;
      if (countInput) countInput.disabled = true;
      countPresets.forEach((btn) => {
        btn.disabled = true;
      });
      setStatus(i18n('aiRecommendThinking', 'AI is finding sites…'));

      try {
        const count = countInput ? countInput.value : DEFAULT_COUNT;
        const sites = await fetchAiBookmarkRecommendations(topic, count);
        showResults(sites, topic);
      } catch (error) {
        console.error('AI recommend failed:', error);
        setStatus(getAiRecommendErrorMessage(error), true);
      } finally {
        busy = false;
        generateBtn.disabled = false;
        topicInput.disabled = false;
        if (countInput) countInput.disabled = false;
        countPresets.forEach((btn) => {
          btn.disabled = false;
        });
      }
    };

    const onAdd = async () => {
      if (busy) return;
      const selected = getSelectedSites();
      if (!selected.length) {
        setStatus(i18n('aiRecommendNoneSelected', 'Select at least one site'), true);
        return;
      }

      busy = true;
      if (addBtn) addBtn.disabled = true;
      setStatus(i18n('aiRecommendAdding', 'Adding bookmarks…'));

      try {
        const added = await addRecommendedBookmarks(folderId, selected);
        cleanup({ added, sites: selected });
      } catch (error) {
        console.error('Failed to add recommendations:', error);
        setStatus(getAiRecommendErrorMessage(error), true);
        busy = false;
        if (addBtn) addBtn.disabled = false;
      }
    };

    const onTopicKeydown = (event) => {
      if (event.key === 'Enter') {
        event.preventDefault();
        onGenerate();
      }
    };

    const syncCountPresets = () => {
      if (!countInput) return;
      const value = String(parseInt(countInput.value, 10) || '');
      countPresets.forEach((btn) => {
        btn.classList.toggle('is-active', btn.dataset.count === value);
      });
    };

    const onCountPresetClick = (event) => {
      if (busy || !countInput) return;
      const btn = event.currentTarget;
      const value = parseInt(btn.dataset.count, 10);
      if (!Number.isFinite(value)) return;
      countInput.value = String(value);
      syncCountPresets();
    };

    const onCountInput = () => {
      syncCountPresets();
    };

    // Reset UI
    if (countInput) {
      countInput.value = String(DEFAULT_COUNT);
      countInput.min = String(MIN_COUNT);
      countInput.max = String(MAX_COUNT);
    }
    syncCountPresets();
    topicInput.value = '';
    listEl.innerHTML = '';
    showInput();
    dialog.style.display = 'block';
    window.setTimeout(() => topicInput.focus(), 0);

    generateBtn.addEventListener('click', onGenerate);
    addBtn?.addEventListener('click', onAdd);
    backBtn?.addEventListener('click', onBack);
    cancelBtn?.addEventListener('click', onCancel);
    closeBtn?.addEventListener('click', onCancel);
    topicInput.addEventListener('keydown', onTopicKeydown);
    countInput?.addEventListener('input', onCountInput);
    countPresets.forEach((btn) => {
      btn.addEventListener('click', onCountPresetClick);
    });
  });
}
