/**
 * groupCvBuilder.js — Per-job CV and cover letter generation
 *
 * Flow:
 *   generate → save HTML to server → send link
 *   client gives feedback → update HTML → same link
 *   client approves → finalize (PDF sent in chat) → status cv_ready / cover_ready
 *
 * HTML stored at: https://vianexu.com/clients/ecobe/cvmaster/docs/{key}.html
 * Key format: {slug}-{jobId}-cv  |  {slug}-{jobId}-cover
 */

import fetch from 'node-fetch';
import PDFDocument from 'pdfkit';
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
import { join } from 'path';
async function readPdfText(filePath) {
  if (!existsSync(filePath)) return '';
  try {
    const { PDFParse } = await import('pdf-parse');
    const parser = new PDFParse({ data: readFileSync(filePath) });
    const result = await parser.getText();
    return (result.text || '').trim();
  } catch { return ''; }
}

const CVMASTER_API = 'https://vianexu.com/clients/ecobe/cvmaster/api.php';

// ── Doc key / URL helpers ─────────────────────────────────────────────────────

export function docKey(slug, jobId, type) {
  // e.g. "tom-smidth_5821622651-jt_123abc-cv"
  return `${slug}-${jobId}-${type}`;
}

export function docUrl(slug, jobId, type) {
  return `https://vianexu.com/clients/ecobe/cvmaster/docs/${docKey(slug, jobId, type)}.html`;
}

// ── Local cache helpers ───────────────────────────────────────────────────────

function localCachePath(scriptDir, key) {
  if (!scriptDir) return null;
  const dir = join(scriptDir, 'cvs-cache');
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
  return join(dir, `${key}.html`);
}

function saveLocalCache(scriptDir, key, html) {
  const p = localCachePath(scriptDir, key);
  if (p) try { writeFileSync(p, html, 'utf8'); } catch { /* best-effort */ }
}

function readLocalCache(scriptDir, key) {
  const p = localCachePath(scriptDir, key);
  if (!p || !existsSync(p)) return null;
  try { return readFileSync(p, 'utf8'); } catch { return null; }
}

// ── PHP API helpers ───────────────────────────────────────────────────────────

async function saveDoc(key, html, scriptDir) {
  saveLocalCache(scriptDir, key, html); // always save locally first
  try {
    const res = await fetch(`${CVMASTER_API}?action=save&lead=${encodeURIComponent(key)}`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({ html }),
      signal:  AbortSignal.timeout(15000),
    });
    return res.ok;
  } catch (e) {
    console.error('[cvbuilder] saveDoc failed:', e.message);
    return false; // local copy already saved above
  }
}

async function getDoc(key, scriptDir) {
  try {
    const res  = await fetch(`${CVMASTER_API}?action=get&lead=${encodeURIComponent(key)}`, { signal: AbortSignal.timeout(10000) });
    const data = await res.json();
    if (data.exists && data.html) return data.html;
  } catch (e) {
    console.error('[cvbuilder] getDoc failed:', e.message);
  }
  // fallback to local cache
  const local = readLocalCache(scriptDir, key);
  if (local) console.warn('[cvbuilder] getDoc: using local cache for', key);
  return local;
}

// ── CV HTML generation ────────────────────────────────────────────────────────

const CV_SYSTEM = `You are an expert career consultant and CV writer for EcoBe.
Generate a complete, professional, ATS-optimised CV in HTML for the client below.

STYLING RULES:
- Inline CSS only (no external sheets, no @import)
- Clean white background (#ffffff), body text #222222
- Headers and accents: #28a745 (EcoBe green)
- Font: Arial, sans-serif — no Google Fonts
- Max width 900px, centered, padding 40px
- Professional and modern — readable without JavaScript
- Keywords from the job description that appear in the CV must be wrapped in <strong> tags so they are visually highlighted

CONTENT RULES — STRUCTURE:
Include these sections in this order:
1. Header (name, contact, LinkedIn)
2. Professional Summary (bullet points — see rules below)
3. Key Competencies (see rules below)
4. Experience (see rules below)
5. Education
6. Skills
7. Languages

TARGET LENGTH: The entire CV body text (excluding HTML/CSS) must be approximately 600 words. Be concise.

PROFESSIONAL SUMMARY:
- Write as bullet points (3–5 bullets), NOT a paragraph
- Each bullet is a short phrase (max 12 words)
- Use quantitative results wherever possible (e.g. "Led team of 12", "Grew revenue 40% YoY")
- Use field-specific terminology — not generic management phrases
- NEVER use the phrase "And that" anywhere in the CV
- Highlight job-description keywords with <strong> tags

KEY COMPETENCIES:
- Add a short section titled "Key Competencies" immediately after the summary
- List 6–8 competencies drawn directly from the job description requirements
- For each competency, show in 1 short line how the candidate meets it, using evidence from their background
- Use the actual vocabulary of the target field — not generic descriptions
- Format as a 2-column table or a grid of short labelled bullets
- ONLY include competencies the candidate genuinely has — NEVER invent
- NEVER list a skill that contradicts or is absent from the experience section

EXPERIENCE:
- Every role must name the company and describe specific projects or initiatives the candidate worked on — NEVER use placeholder-like statements ("contributed to various projects", "supported strategic initiatives")
- Each bullet must describe what the candidate changed, for whom, and what the result was
- Shorter sentences preferred — clarity over polish
- Use strong action verbs (Led, Built, Delivered, Increased, Reduced, Cut, Launched…)
- Use field-specific language appropriate to the candidate's industry — not generic corporate phrasing
- Include at least one quantified achievement per role where data exists
- Write bullets so the candidate can expand on them in an interview — real context, not vague summaries
- Keep each role to 3–5 bullet points maximum
- Highlight job-description keywords with <strong> tags

SKILLS:
- Include ONLY skills the candidate actually has (from their profile and raw CV text)
- Ensure every listed skill is backed by at least one experience entry — no orphan skills
- Prioritise skills that also appear in the job description
- NEVER invent or add skills not evidenced in the inputs

LANGUAGE & TONE:
- Short sentences. Real detail. No filler.
- AVOID: "passionate about", "dynamic", "results-driven", "synergies", "comprehensive strategies", "And that"
- AVOID long polished sentences with no concrete detail
- DO: name companies, projects, tools, numbers, team sizes, outcomes

ABSOLUTE RULES:
- Do NOT include any information not present in the inputs
- NEVER invent companies, titles, dates, achievements, certifications, or skills
- NEVER use the phrase "And that"
- Return ONLY the HTML document (<!DOCTYPE html> ... </html>). No explanation.`;

export async function generateJobCv(slug, jobId, job, cvmaster, careerStrategy, aiFn, scriptDir = '') {
  const cvPersonalText = scriptDir ? await readPdfText(join(scriptDir, 'mentees', slug, 'cv-personal.pdf')) : '';
  const cvLinkedinText = scriptDir ? await readPdfText(join(scriptDir, 'mentees', slug, 'cv-linkedin.pdf')) : '';

  const input = {
    targetJob: {
      title:       job.title,
      company:     job.company,
      location:    job.location,
      url:         job.url,
      description: job.description || job.snippet || null,
    },
    clientProfile: {
      name:        cvmaster.leadName,
      headline:    cvmaster.headline,
      summary:     cvmaster.summary,
      contact:     cvmaster.contact,
      experience:  cvmaster.experience,
      education:   cvmaster.education,
      skills:      cvmaster.skills,
      languages:   cvmaster.languages,
      certifications: cvmaster.certifications,
      keyStrengths: cvmaster.keyStrengths,
    },
    positioning: careerStrategy.search?.positioning || cvmaster.narrative,
    targetRoles: careerStrategy.primaryRoles || cvmaster.targetRoles,
    ...(cvPersonalText && { personalCvRawText: cvPersonalText }),
    ...(cvLinkedinText && { linkedinCvRawText: cvLinkedinText }),
  };

  const html = await aiFn(CV_SYSTEM, [{ role: 'user', content: JSON.stringify(input, null, 2) }], 'gpt-4.1');
  if (!html || !html.includes('<html')) throw new Error('CV generation returned invalid HTML');

  const key   = docKey(slug, jobId, 'cv');
  const saved = await saveDoc(key, html, scriptDir);
  if (!saved) console.warn('[cvbuilder] CV saved locally only (server save failed)');

  return docUrl(slug, jobId, 'cv');
}

// ── Cover letter HTML generation ──────────────────────────────────────────────

const COVER_SYSTEM = `You are an expert career consultant writing a cover letter for EcoBe.
Generate a tailored, human cover letter in HTML.

STYLING RULES:
- Inline CSS only. White background. #28a745 for accents. Arial, sans-serif.
- Max width 750px, centered, padding 40px. Clean and minimal.
- Return ONLY the HTML document. No explanation.

CONTENT RULES — STRUCTURE:
3–4 short paragraphs:
1. Opening — a direct statement of why this candidate fits this specific role or company. NOT "I am writing to express my interest in…"
2. Why this company — one specific thing about the company, product, market, or team that is relevant. Not generic praise.
3. What you bring — name one concrete project or result from the candidate's background that maps to this role. Say what they changed, for whom, and what happened. Prove the claim — don't just state it.
4. Closing — short, direct next step. No "I am confident in my ability to contribute…"

LANGUAGE & TONE:
- Short sentences. Direct. No filler.
- First person, grounded — not arrogant, not overly humble
- Match the language (English/French/Portuguese/Spanish) of the client's profile
- AVOID these phrases entirely:
  • "I am writing to express my interest in…"
  • "I am confident in my ability to contribute…"
  • "Passionate", "dynamic", "excited to join", "results-driven"
  • "Managed comprehensive integration strategies…"
  • Long polished sentences with no real detail
- DO:
  • Start with a direct reason you fit this role or market
  • Name one relevant result or project instead of self-praise
  • Use a concrete trait only if the experience proves it
  • Say what you changed, for whom, and what happened
  • Name the company specifically — never use placeholder-like statements

ABSOLUTE RULES:
- NEVER invent experience, projects, results, or details not present in the inputs
- NEVER use a generic opening line — the first sentence must be specific to this role/company`;

export async function generateCoverLetter(slug, jobId, job, cvmaster, careerStrategy, aiFn, scriptDir = '') {
  const input = {
    targetJob: { title: job.title, company: job.company, location: job.location },
    clientName: cvmaster.leadName,
    positioning: careerStrategy.search?.positioning || cvmaster.narrative,
    keyStrengths: cvmaster.keyStrengths,
    relevantExperience: cvmaster.experience?.slice(0, 2),
    salaryTarget: cvmaster.salaryTarget || careerStrategy.salaryTarget,
  };

  const html = await aiFn(COVER_SYSTEM, [{ role: 'user', content: JSON.stringify(input, null, 2) }], 'gpt-4.1');
  if (!html || !html.includes('<html')) throw new Error('Cover letter generation returned invalid HTML');

  const key   = docKey(slug, jobId, 'cover');
  const saved = await saveDoc(key, html, scriptDir);
  if (!saved) console.warn('[cvbuilder] Cover letter saved locally only (server save failed)');

  return docUrl(slug, jobId, 'cover');
}

// ── Update a doc based on feedback ────────────────────────────────────────────

export async function updateDoc(slug, jobId, type, feedback, aiFn, scriptDir = '') {
  const key         = docKey(slug, jobId, type);
  const currentHtml = await getDoc(key, scriptDir);
  if (!currentHtml) throw new Error(`${type} not found on server or local cache`);

  const label = type === 'cv' ? 'CV' : 'cover letter';
  const prompt = `You are updating a ${label} HTML document based on feedback.

FEEDBACK: "${feedback}"

CURRENT ${label.toUpperCase()}:
${currentHtml}

Apply ONLY the requested changes. Keep everything else identical.
Return ONLY the complete updated HTML document.`;

  const updated = await aiFn(
    `You are a precise ${label} editor. Apply the requested changes and return the complete updated HTML.`,
    [{ role: 'user', content: prompt }],
    'gpt-4.1'
  );
  if (!updated || !updated.includes('<html')) throw new Error('Update returned invalid HTML');

  const saved = await saveDoc(key, updated, scriptDir);
  if (!saved) console.warn('[cvbuilder] Update saved locally only (server save failed)');

  return docUrl(slug, jobId, type);
}

// ── Classify a message as feedback/approved/other ─────────────────────────────

export async function classifyDocMessage(text, type, aiFn) {
  const label = type === 'cv' ? 'CV' : 'cover letter';
  const result = await aiFn(
    `You classify WhatsApp messages from job seekers who are currently reviewing their ${label}.`,
    [{
      role: 'user',
      content: `The client is currently reviewing their ${label}. They just sent this message: "${text}"

Classify it as:
- APPROVED: they are happy with it and want to proceed (e.g. "looks good", "ok", "perfect", "I want to apply", "fits my expectations", "let's go", "send it", "yes", "submit it", etc.)
- FEEDBACK: they request a specific change or correction to the ${label}
- OTHER: completely unrelated to the ${label} (e.g. asking about a different topic)

Note: if they mention wanting to apply for the job or that the ${label} fits them, that counts as APPROVED.

Reply with ONLY one word: APPROVED, FEEDBACK, or OTHER.`
    }],
    'gpt-4.1-mini'
  );
  const r = result?.trim().toUpperCase();
  if (r?.includes('APPROVED')) return 'approved';
  if (r?.includes('FEEDBACK')) return 'feedback';
  return 'other';
}

// ── Finalize: generate PDF and save locally ───────────────────────────────────

export async function finalizeToPdf(slug, jobId, type, leadName, scriptDir) {
  const key  = docKey(slug, jobId, type);
  const html = await getDoc(key, scriptDir);
  if (!html) throw new Error(`${type} not found for PDF generation`);

  // Strip HTML tags for plain text PDF (pdfkit-based)
  const text = html
    .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
    .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
    .replace(/<[^>]+>/g, ' ')
    .replace(/&nbsp;/g, ' ')
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/\s{2,}/g, ' ')
    .trim();

  const pdfBuffer = await buildPdf(text, leadName, type);

  // Save locally
  const cvsDir = join(scriptDir, 'mentees', slug, 'cvs');
  if (!existsSync(cvsDir)) mkdirSync(cvsDir, { recursive: true });
  const fileName = `${jobId}-${type}.pdf`;
  writeFileSync(join(cvsDir, fileName), pdfBuffer);

  return { pdfBuffer, fileName };
}

function buildPdf(text, clientName, type) {
  return new Promise((resolve, reject) => {
    const doc    = new PDFDocument({ margin: 50 });
    const chunks = [];
    doc.on('data',  c  => chunks.push(c));
    doc.on('end',   () => resolve(Buffer.concat(chunks)));
    doc.on('error', reject);

    const label = type === 'cv' ? 'CURRICULUM VITAE' : 'COVER LETTER';
    doc.fontSize(18).fillColor('#28a745').font('Helvetica-Bold').text(label, { align: 'center' });
    doc.moveDown(0.3);
    doc.fontSize(13).fillColor('#333').font('Helvetica').text(clientName, { align: 'center' });
    doc.moveDown(0.3);
    doc.fontSize(9).fillColor('#999').text(new Date().toLocaleDateString('en-GB', { day: '2-digit', month: 'long', year: 'numeric' }), { align: 'center' });
    doc.moveDown(0.8);
    doc.moveTo(50, doc.y).lineTo(545, doc.y).strokeColor('#28a745').stroke();
    doc.moveDown(0.8);

    for (const line of text.split(/\.\s+/)) {
      const t = line.trim();
      if (t.length < 3) continue;
      doc.fontSize(10).fillColor('#222').font('Helvetica').text(t + '.', { lineGap: 4 });
    }
    doc.end();
  });
}
