import https from 'node:https';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { textToAdoHtml } from '../utils/ado-formatter.js';

const SDD_DIR = '.sdd';

export function sanitizeAdoOrgUrl(raw) {
  try {
    const u = new URL(raw.trim());
    if (u.hostname === 'dev.azure.com') {
      const org = u.pathname.split('/').filter(Boolean)[0];
      return org ? `https://dev.azure.com/${org}` : 'https://dev.azure.com';
    }
    return `${u.protocol}//${u.hostname}`;
  } catch { return raw.trim().replace(/\/$/, ''); }
}

export function adoRequest(orgUrl, pat, method, fullUrl, body) {
  return new Promise((resolve, reject) => {
    const auth = Buffer.from(':' + pat).toString('base64');
    const u = new URL(fullUrl);
    const postData = body ? JSON.stringify(body) : null;
    const req = https.request({
      hostname: u.hostname,
      path: u.pathname + u.search,
      method,
      headers: {
        'Authorization': 'Basic ' + auth,
        'Content-Type': method === 'PATCH' ? 'application/json-patch+json' : 'application/json',
        'Accept': 'application/json',
        ...(postData ? { 'Content-Length': Buffer.byteLength(postData) } : {}),
      },
    }, res => {
      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        const text = Buffer.concat(chunks).toString();
        try { resolve({ status: res.statusCode, body: JSON.parse(text) }); }
        catch { resolve({ status: res.statusCode, body: text }); }
      });
    });
    req.on('error', reject);
    if (postData) req.write(postData);
    req.end();
  });
}

export async function adoFindEpic(orgUrl, project, pat, title) {
  const url = `${orgUrl}/${encodeURIComponent(project)}/_apis/wit/wiql?api-version=7.1`;
  const res = await adoRequest(orgUrl, pat, 'POST', url, {
    query: `SELECT [System.Id],[System.Title] FROM WorkItems WHERE [System.WorkItemType]='Epic' AND [System.Title]='${title.replace(/'/g, "''")}' AND [System.TeamProject]='${project.replace(/'/g, "''")}'`,
  });
  if (res.status === 200 && res.body.workItems?.length) {
    return res.body.workItems[0].id;
  }
  return null;
}

export async function adoCreateWorkItem(orgUrl, project, pat, type, title, description, acceptanceCriteria, parentId, areaPath, tag, iteration, assignedTo, customFields) {
  const ops = [
    { op: 'add', path: '/fields/System.Title', value: title },
  ];
  if (description) {
    ops.push({ op: 'add', path: '/fields/System.Description', value: textToAdoHtml(description) });
  }
  if (acceptanceCriteria) {
    ops.push({ op: 'add', path: '/fields/Microsoft.VSTS.Common.AcceptanceCriteria', value: textToAdoHtml(acceptanceCriteria) });
  }
  if (areaPath) {
    ops.push({ op: 'add', path: '/fields/System.AreaPath', value: areaPath });
  }
  if (iteration) {
    ops.push({ op: 'add', path: '/fields/System.IterationPath', value: iteration });
  }
  if (tag) {
    ops.push({ op: 'add', path: '/fields/System.Tags', value: tag });
  }
  if (assignedTo) {
    ops.push({ op: 'add', path: '/fields/System.AssignedTo', value: assignedTo });
  }
  if (customFields && typeof customFields === 'object') {
    for (const [ref, val] of Object.entries(customFields)) {
      if (ref && val !== undefined && val !== null && val !== '') {
        ops.push({ op: 'add', path: '/fields/' + ref, value: val });
      }
    }
  }
  if (parentId) {
    const parentUrl = `${orgUrl}/${encodeURIComponent(project)}/_apis/wit/workItems/${parentId}`;
    ops.push({ op: 'add', path: '/relations/-', value: {
      rel: 'System.LinkTypes.Hierarchy-Reverse',
      url: parentUrl,
    }});
  }
  const url = `${orgUrl}/${encodeURIComponent(project)}/_apis/wit/workitems/$${encodeURIComponent(type)}?api-version=7.1`;
  return adoRequest(orgUrl, pat, 'PATCH', url, ops);
}

export async function adoUploadAttachment(orgUrl, project, pat, fileName, content) {
  return new Promise((resolve, reject) => {
    const auth = Buffer.from(':' + pat).toString('base64');
    const u = new URL(`${orgUrl}/${encodeURIComponent(project)}/_apis/wit/attachments?fileName=${encodeURIComponent(fileName)}&api-version=7.1`);
    const buf = Buffer.from(content, 'utf8');
    const req = https.request({
      hostname: u.hostname, path: u.pathname + u.search, method: 'POST',
      headers: { 'Authorization': 'Basic ' + auth, 'Content-Type': 'application/octet-stream', 'Accept': 'application/json', 'Content-Length': buf.length },
    }, res => {
      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        const text = Buffer.concat(chunks).toString();
        try { resolve({ status: res.statusCode, body: JSON.parse(text) }); } catch { resolve({ status: res.statusCode, body: text }); }
      });
    });
    req.on('error', reject);
    req.write(buf);
    req.end();
  });
}

export async function adoAddQAComment(orgUrl, project, pat, workItemId, results) {
  const { summary, scenarios, executedAt } = results;
  const dateStr = new Date(executedAt).toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo' });
  const statusIcon = summary.fail === 0 ? '✅' : summary.pass === 0 ? '❌' : '⚠️';
  const rows = scenarios.map(s =>
    `<tr><td>${s.title}</td><td>${s.status === 'pass' ? '✅ Passou' : '❌ Falhou'}</td><td style="color:#666;font-size:12px">${s.error ? s.error.slice(0, 120) : '—'}</td></tr>`
  ).join('');
  const html = `<b>${statusIcon} QA Automatizado — SDD-FP</b><br>`
    + `Executado em: ${dateStr}<br>`
    + `<b>Resultado: ${summary.pass}/${summary.total} cenários aprovados</b><br><br>`
    + `<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;font-size:13px">`
    + `<tr style="background:#f5f5f5"><th>Cenário</th><th>Status</th><th>Observação</th></tr>`
    + rows
    + `</table>`;
  const url = `${orgUrl}/${encodeURIComponent(project)}/_apis/wit/workitems/${workItemId}?api-version=7.1`;
  return adoRequest(orgUrl, pat, 'PATCH', url, [{ op: 'add', path: '/fields/System.History', value: html }]);
}

export function parseWorkItemId(input) {
  const m = String(input).match(/[/_](\d+)(?:[?#]|$)/);
  return m ? parseInt(m[1]) : (parseInt(input) || null);
}

export async function parseRequirementsForAdo(specName, cwd) {
  const filePath = path.join(cwd, SDD_DIR, 'specs', specName, 'requirements.md');
  const content = await readFile(filePath, 'utf8');
  const stories = [];
  const sections = content.split(/^###\s+/m).slice(1);
  for (const section of sections) {
    const lines = section.split('\n');
    const rawTitle = lines[0].trim();
    const title = rawTitle.replace(/^(Requirement|Requisito)\s+\d+[:.]\s*/i, '');
    const objMatch = section.match(/\*\*(?:Objective|Objetivo)[*:]+\s*(.+?)(?:\n|$)/i);
    const description = objMatch ? objMatch[1].trim() : '';
    const acMatch = section.match(/#{4}\s+(?:Acceptance Criteria|Crit[eé]rios de Aceite)([\s\S]*?)(?:---|#{3}|$)/i);
    const criteria = acMatch ? acMatch[1].trim() : '';
    if (title) stories.push({ title, description, criteria });
  }
  return stories;
}

export async function parseRequirementsForQA(specName, cwd) {
  const filePath = path.join(cwd, SDD_DIR, 'specs', specName, 'requirements.md');
  const content = await readFile(filePath, 'utf8');
  const scenarios = [];
  const sections = content.split(/^###\s+/m).slice(1);
  for (const section of sections) {
    const lines = section.split('\n');
    const title = lines[0].trim().replace(/^(Requirement|Requisito)\s+\d+[:.]\s*/i, '');
    const objMatch = section.match(/\*\*(?:Objective|Objetivo)[*:]+\s*(.+?)(?:\n|$)/i);
    const acMatch = section.match(/#{4}\s+(?:Acceptance Criteria|Crit[eé]rios de Aceite)([\s\S]*?)(?:---|#{3}|$)/i);
    if (title) scenarios.push({
      title,
      objective: objMatch ? objMatch[1].trim() : '',
      criteria: acMatch ? acMatch[1].trim() : '',
    });
  }
  return scenarios;
}
