/*
 * Sponzza Worker (Cloudflare). Three endpoints:
 *   POST /soc-match       { title, description }                      -> { soc, title, eligible, confidence, reason }
 *   POST /resume-extract  { pdf_base64, media_type }                  -> { text }
 *   POST /cv-match        { resume_text, job_title, job_description, soc_title } -> { score, verdict, strengths[], gaps[] }
 *
 * ANTHROPIC_API_KEY lives only in the Worker secret.
 */
import { SOC_LIST } from './soc-list.js';

const HAIKU = 'claude-haiku-4-5-20251001';
const SONNET = 'claude-sonnet-4-6';

const CORS = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type',
};
function json(body, status = 200) {
  return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json', ...CORS } });
}

async function anthropic(env, payload) {
  const r = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-api-key': env.ANTHROPIC_API_KEY, 'anthropic-version': '2023-06-01' },
    body: JSON.stringify(payload),
  });
  if (!r.ok) { const t = await r.text(); throw new Error('anthropic ' + r.status + ': ' + t.slice(0, 200)); }
  const data = await r.json();
  return (data.content || []).map(b => b.text || '').join('').trim();
}
function parseJson(text) { return JSON.parse(text.replace(/```json|```/g, '').trim()); }

async function socMatch(env, body) {
  const title = (body.title || '').slice(0, 200);
  const description = (body.description || '').slice(0, 2500);
  if (!title) return json({ error: 'title required' }, 400);
  const allowed = SOC_LIST.map(s => s.code + '\t' + s.title).join('\n');
  const system = 'You classify UK jobs into SOC 2020 occupation codes for Skilled Worker visa sponsorship. ' +
    'Choose the single best-matching occupation from the ALLOWED list only. Never invent a code. ' +
    'If the role is clearly not skilled/eligible or nothing fits, set soc to null. ' +
    'Respond with ONLY minified JSON: {"soc":"<code>"|null,"title":"<official title>","eligible":<bool>,"confidence":<0-1>,"reason":"<short>"}';
  const user = 'JOB TITLE: ' + title + '\n\nDESCRIPTION:\n' + (description || '(none)') +
    '\n\nALLOWED OCCUPATIONS (code<TAB>title):\n' + allowed;
  const text = await anthropic(env, { model: HAIKU, max_tokens: 300, system, messages: [{ role: 'user', content: user }] });
  const parsed = parseJson(text);
  if (parsed.soc && !SOC_LIST.some(s => s.code === String(parsed.soc))) { parsed.soc = null; parsed.reason = 'No confident SOC match'; }
  return json(parsed);
}

async function resumeExtract(env, body) {
  const b64 = body.pdf_base64;
  if (!b64) return json({ error: 'pdf_base64 required' }, 400);
  const media = body.media_type || 'application/pdf';
  const content = [
    { type: 'document', source: { type: 'base64', media_type: media, data: b64 } },
    { type: 'text', text: 'Extract all readable text from this CV / résumé. Return ONLY the plain text content, no commentary, preserving rough structure (headings, bullets as lines).' },
  ];
  const text = await anthropic(env, { model: SONNET, max_tokens: 4000, messages: [{ role: 'user', content }] });
  return json({ text: text.slice(0, 12000) });
}

async function cvMatch(env, body) {
  const resume = (body.resume_text || '').slice(0, 12000);
  const jobTitle = (body.job_title || '').slice(0, 200);
  const jobDesc = (body.job_description || '').slice(0, 3000);
  const socTitle = (body.soc_title || '').slice(0, 120);
  if (!resume || !jobTitle) return json({ error: 'resume_text and job_title required' }, 400);
  const system = 'You are a UK hiring expert assessing how well a candidate\'s CV fits a specific job. ' +
    'Be honest and specific, grounded in the CV and job description. ' +
    'Respond with ONLY minified JSON: {"score":<0-100 integer>,"verdict":"strong fit"|"worth tailoring"|"likely a stretch","strengths":["..","..",".."],"gaps":["..","..",".."]}. ' +
    'Exactly 3 short strengths and 3 short gaps.';
  const user = 'JOB TITLE: ' + jobTitle + (socTitle ? '\nSOC OCCUPATION: ' + socTitle : '') +
    '\n\nJOB DESCRIPTION:\n' + (jobDesc || '(none)') + '\n\nCANDIDATE CV:\n' + resume;
  const text = await anthropic(env, { model: SONNET, max_tokens: 700, system, messages: [{ role: 'user', content: user }] });
  return json(parseJson(text));
}

export default {
  async fetch(request, env) {
    if (request.method === 'OPTIONS') return new Response(null, { headers: CORS });
    if (request.method !== 'POST') return json({ error: 'POST only' }, 405);
    let body; try { body = await request.json(); } catch { return json({ error: 'bad json' }, 400); }
    const path = new URL(request.url).pathname.replace(/\/$/, '');
    try {
      if (path.endsWith('/resume-extract')) return await resumeExtract(env, body);
      if (path.endsWith('/cv-match')) return await cvMatch(env, body);
      return await socMatch(env, body); // default + /soc-match
    } catch (e) {
      return json({ error: String(e.message || e).slice(0, 240) }, 502);
    }
  },
};
