/**
 * End-to-end API QA harness — Gventure Technology demo scenarios.
 * Does not call live Meta/LinkedIn publish. Does not print secrets.
 *
 * Run: npx tsx scripts/qa-gventure-e2e.ts [baseUrl]
 */
import { writeFileSync, mkdirSync } from "fs";
import path from "path";

const BASE = (process.argv[2] ?? "http://127.0.0.1:3044").replace(/\/$/, "");

type Status = "PASS" | "FAIL" | "BLOCKED";
type Severity = "critical" | "high" | "medium" | "low" | "info";

interface CaseResult {
  id: string;
  feature: string;
  name: string;
  status: Status;
  detail?: string;
  severity?: Severity;
  httpStatus?: number;
  ms?: number;
}

const results: CaseResult[] = [];
const cookieJar = new Map<string, string>();

function record(r: CaseResult) {
  results.push(r);
  const mark = r.status.padEnd(7);
  console.log(`${mark} [${r.feature}] ${r.id} ${r.name}${r.detail ? ` — ${r.detail}` : ""}`);
}

function parseSetCookie(headers: Headers) {
  const raw = headers.getSetCookie?.() ?? [];
  const fallback = headers.get("set-cookie");
  const list = raw.length ? raw : fallback ? [fallback] : [];
  for (const c of list) {
    const [pair] = c.split(";");
    const eq = pair.indexOf("=");
    if (eq > 0) cookieJar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
  }
}

function cookieHeader() {
  return [...cookieJar.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
}

async function req(
  method: string,
  urlPath: string,
  options?: { body?: unknown; auth?: boolean; headers?: Record<string, string> },
) {
  const started = Date.now();
  const headers: Record<string, string> = {
    Accept: "application/json,text/html,*/*",
    ...(options?.headers ?? {}),
  };
  if (options?.body !== undefined) headers["Content-Type"] = "application/json";
  if (options?.auth !== false && cookieJar.size) headers.Cookie = cookieHeader();

  const res = await fetch(`${BASE}${urlPath}`, {
    method,
    headers,
    body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
    redirect: "manual",
  });
  parseSetCookie(res.headers);
  const text = await res.text();
  let json: unknown = null;
  try {
    json = JSON.parse(text);
  } catch {
    /* html or empty */
  }
  return { res, text, json, ms: Date.now() - started };
}

const GV = {
  name: "Gventure Technology",
  website: "https://gventure.net",
  logo: "https://gventure.net/assets/img/logo/logo-gv.png",
  tagline: "Its All About Technology Inspiration",
  industry: "IT Services / VoIP / AI Software",
  services: [
    "AI solutions",
    "CRM",
    "Predictive dialers",
    "Call tracking",
    "AI agents",
    "Automation",
    "Custom software development",
    "VoIP / PBX / CPaaS",
  ],
};

function gventureProjectId() {
  return `proj_gv_qa_${Date.now().toString(36)}`;
}

async function main() {
  console.log(`\n=== Gventure E2E QA against ${BASE} ===\n`);

  // Health / pages
  {
    const r = await req("GET", "/login", { auth: false });
    record({
      id: "UI-01",
      feature: "Auth UI",
      name: "GET /login renders",
      status: r.res.status === 200 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      ms: r.ms,
      detail: `HTTP ${r.res.status}`,
      severity: r.res.status === 200 ? undefined : "critical",
    });
  }
  {
    const r = await req("GET", "/dashboard", { auth: false });
    const redirected = r.res.status === 307 || r.res.status === 302;
    const loc = r.res.headers.get("location") ?? "";
    record({
      id: "SEC-01",
      feature: "Auth middleware",
      name: "Unauthed /dashboard redirects to login",
      status: redirected && loc.includes("/login") ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: `status=${r.res.status} location=${loc}`,
      severity: "high",
    });
  }
  {
    const r = await req("GET", "/api/projects", { auth: false });
    record({
      id: "SEC-02",
      feature: "API auth",
      name: "GET /api/projects without session → 401",
      status: r.res.status === 401 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      severity: "critical",
    });
  }
  {
    const r = await req("GET", "/api/cron/scheduled-publish", { auth: false });
    record({
      id: "SEC-03",
      feature: "Cron security",
      name: "Cron without secret → 401",
      status: r.res.status === 401 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      severity: "critical",
    });
  }
  {
    const r = await req("POST", "/api/auth/login", {
      auth: false,
      body: { email: "wrong@example.com", password: "nope" },
    });
    record({
      id: "AUTH-01",
      feature: "Login",
      name: "Invalid credentials → 401",
      status: r.res.status === 401 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
    });
  }
  {
    const r = await req("POST", "/api/auth/login", {
      auth: false,
      body: { email: "", password: "" },
    });
    record({
      id: "AUTH-02",
      feature: "Login validation",
      name: "Empty login → 400",
      status: r.res.status === 400 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
    });
  }

  // Demo login — try common demo creds from .env.example (not reading .env.local)
  const loginCandidates = [
    { email: "sarah@postsync.pro", password: "demo" },
    { email: process.env.AUTH_DEMO_EMAIL ?? "", password: process.env.AUTH_DEMO_PASSWORD ?? "" },
  ].filter((c) => c.email && c.password);

  let loggedIn = false;
  for (const cred of loginCandidates) {
    cookieJar.clear();
    const r = await req("POST", "/api/auth/login", { auth: false, body: cred });
    if (r.res.status === 200 && cookieJar.has("postsync_auth")) {
      loggedIn = true;
      record({
        id: "AUTH-03",
        feature: "Login",
        name: "Demo login succeeds",
        status: "PASS",
        httpStatus: 200,
        detail: "session cookie issued",
      });
      break;
    }
  }
  if (!loggedIn) {
    // Signup path creates ephemeral user (known weak gate — still usable for QA)
    cookieJar.clear();
    const r = await req("PUT", "/api/auth/login", {
      auth: false,
      body: {
        name: "Gventure QA Tester",
        email: `qa.gventure.${Date.now()}@example.com`,
        password: "qa12Secure!",
      },
    });
    loggedIn = r.res.status === 200 && cookieJar.has("postsync_auth");
    record({
      id: "AUTH-03",
      feature: "Login",
      name: "Demo login / signup fallback",
      status: loggedIn ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: loggedIn
        ? "Authenticated via demo or signup path"
        : `Could not authenticate HTTP ${r.res.status}`,
      severity: loggedIn ? "high" : "critical",
    });
    // Weak password probe still recorded below when demo login succeeds;
    // when only signup is used, still assert 4-char passwords are rejected.
    {
      const saved = new Map(cookieJar);
      cookieJar.clear();
      const weak = await req("PUT", "/api/auth/login", {
        auth: false,
        body: { name: "Weak", email: `weak.${Date.now()}@ex.com`, password: "abcd" },
      });
      record({
        id: "SEC-04",
        feature: "Auth security",
        name: "Open signup rejects weak passwords",
        status: weak.res.status === 400 ? "PASS" : "FAIL",
        httpStatus: weak.res.status,
        detail:
          weak.res.status === 200
            ? "Accepted password length 4 and issued session"
            : `HTTP ${weak.res.status}`,
        severity: "high",
      });
      cookieJar.clear();
      for (const [k, v] of saved) cookieJar.set(k, v);
    }
  } else {
    // Still probe weak signup as security case (separate cookies temporarily)
    const saved = new Map(cookieJar);
    cookieJar.clear();
    const weak = await req("PUT", "/api/auth/login", {
      auth: false,
      body: { name: "Weak", email: `weak.${Date.now()}@ex.com`, password: "abcd" },
    });
    record({
      id: "SEC-04",
      feature: "Auth security",
      name: "Open signup rejects weak passwords",
      status: weak.res.status === 400 ? "PASS" : "FAIL",
      httpStatus: weak.res.status,
      detail:
        weak.res.status === 200
          ? "Accepted password length 4 and issued session"
          : `HTTP ${weak.res.status}`,
      severity: "high",
    });
    cookieJar.clear();
    for (const [k, v] of saved) cookieJar.set(k, v);
  }

  if (!loggedIn) {
    console.log("\nBLOCKED: cannot continue authenticated journeys\n");
    finish();
    return;
  }

  {
    const r = await req("GET", "/api/auth/session");
    const ok =
      r.res.status === 200 &&
      Boolean((r.json as { authed?: boolean } | null)?.authed);
    record({
      id: "AUTH-04",
      feature: "Session",
      name: "GET /api/auth/session when authed",
      status: ok ? "PASS" : "FAIL",
      httpStatus: r.res.status,
    });
  }

  // Gventure project snapshot
  const projectId = gventureProjectId();
  const now = new Date().toISOString();
  const snapshot = {
    activeProjectId: projectId,
    projects: [
      {
        id: projectId,
        name: GV.name,
        website: GV.website,
        industry: GV.industry,
        timezone: "Asia/Kolkata",
        countries: ["IN", "US", "AE"],
        status: "active",
        createdAt: now,
        updatedAt: now,
      },
    ],
    dataByProjectId: {
      [projectId]: {
        brand: {
          companyName: GV.name,
          tagline: GV.tagline,
          industry: GV.industry,
          brandVoice: "Professional, innovative, client-first",
          logoDataUrl: null,
          logoFileName: "logo-gv.png",
          colors: { primary: "#0EA5E9", secondary: "#84CC16", accent: "#F97316" },
        },
        context: {
          name: "Gventure Connected Sequence",
          saved: true,
          posts: [
            {
              id: "seq-1",
              title: "Problem — fragmented call ops",
              summary: "Customer pain around dialers and missed follow-ups",
              tag: "Problem",
              content:
                "Problem: Call centers struggle with fragmented dialers, CRM, and AI tools.\nImpact: Missed SLAs and slow follow-up.\n#GventureTechnology #VoIP #AIAgents",
            },
          ],
          businessContext: {
            companyName: GV.name,
            description: GV.tagline,
            industry: GV.industry,
            products: ["Predictive dialers", "CRM"],
            services: GV.services.slice(0, 6),
            features: ["VoIP", "AI agents", "Call tracking"],
            audience:
              "Call centers, enterprises, SaaS providers needing VoIP, dialers, CRM, AI automation",
            locations: ["India", "Global"],
            customerProblems: ["Fragmented dialer stacks", "Missed follow-ups"],
            painPoints: ["Manual ops", "Low visibility"],
            businessImpact: ["Lost conversions", "Higher support cost"],
            differentiators: ["15+ years VoIP/IT expertise"],
            benefits: ["Unified communication stack"],
            useCases: ["Contact center modernization"],
            websiteClaims: [],
            verifiedClaims: [],
            missingInformation: [],
            contact: {
              phone: "919911837377",
              email: "info@gventure.us",
              address: "missing",
            },
            fieldSources: {},
            confirmed: true,
            confirmedAt: now,
          },
          contentStrategy: {
            themes: ["AI & automation", "VoIP & dialer excellence"],
            goals: ["Educate buyers", "Differentiate Gventure"],
            sequenceStrategy:
              "Connected arc: problem → cause → impact → scenario → mistakes → insight → solution → how it works → proof → CTA.",
          },
          storytellingFrameworkId: "customer-journey-v1",
        },
        schedule: {
          postsPerDay: 2,
          saved: true,
          scheduleSeed: 42,
        },
        automation: {
          "high-score": false,
          "loop-sequence": false,
          "smart-timing": true,
          "auto-approval-publish": false,
        },
        accounts: [],
        selectedPostId: "seq-1",
      },
    },
  };

  {
    const r = await req("PUT", "/api/projects", { body: snapshot });
    record({
      id: "PROJ-01",
      feature: "Projects",
      name: "Save Gventure project snapshot",
      status: r.res.status === 200 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      ms: r.ms,
      detail: r.res.status === 200 ? projectId : JSON.stringify(r.json).slice(0, 200),
      severity: r.res.status === 200 ? undefined : "critical",
    });
  }
  {
    const r = await req("GET", "/api/projects");
    const projects =
      (r.json as { snapshot?: { projects?: Array<{ id: string; name: string }> } } | null)?.snapshot
        ?.projects ?? [];
    const found = projects.some((p) => p.id === projectId);
    record({
      id: "PROJ-02",
      feature: "Projects",
      name: "Load projects includes Gventure",
      status: r.res.status === 200 && found ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: found ? GV.name : `count=${projects.length}`,
    });
  }

  // Website intelligence — gventure.net
  {
    const r = await req("POST", "/api/website/analyze", {
      body: {
        url: GV.website,
        projectId,
        forceRefresh: true,
        project: snapshot.projects[0],
        brand: snapshot.dataByProjectId[projectId].brand,
      },
    });
    const ok = r.res.status === 200;
    const body = r.json as { error?: string; website?: unknown; brand?: unknown } | null;
    record({
      id: "CTX-01",
      feature: "Website intelligence",
      name: "Analyze https://gventure.net",
      status: ok ? "PASS" : r.res.status >= 500 ? "FAIL" : "BLOCKED",
      httpStatus: r.res.status,
      ms: r.ms,
      detail: ok
        ? `ok in ${r.ms}ms`
        : (body?.error ?? `HTTP ${r.res.status}`).slice(0, 240),
      severity: ok ? undefined : "high",
    });
  }
  {
    const r = await req("POST", "/api/website/analyze", { body: {} });
    record({
      id: "CTX-02",
      feature: "Website intelligence",
      name: "Missing URL → 400",
      status: r.res.status === 400 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
    });
  }

  // Manual content — Gventure AI dialer / CRM posts
  const captions = [
    {
      title: "AI Agents for Modern Call Centers",
      caption:
        "Gventure Technology builds AI agents that qualify leads, summarize calls, and hand off to humans — paired with CRM sync so every conversation becomes actionable. Ready to automate without losing the human touch?",
      platforms: ["linkedin", "facebook"],
    },
    {
      title: "Predictive Dialers that Scale",
      caption:
        "From FreeSWITCH dialers to cloud PBX, Gventure helps teams reach more customers with reliable predictive dialing, IVR, and call tracking. Built for high-volume operations.",
      platforms: ["linkedin"],
    },
    {
      title: "Invalid empty",
      caption: "",
      platforms: ["linkedin"],
    },
  ];

  let manualPostId: string | null = null;
  for (let i = 0; i < captions.length; i++) {
    const c = captions[i];
    const r = await req("POST", "/api/content/manual", {
      body: {
        projectId,
        title: c.title,
        caption: c.caption,
        platforms: c.platforms,
        imageUrl: GV.logo,
      },
    });
    if (i < 2) {
      const post = (r.json as { post?: { id?: string } } | null)?.post;
      if (post?.id) manualPostId = post.id;
      record({
        id: `MAN-${i + 1}`,
        feature: "Manual content",
        name: `Create post: ${c.title}`,
        status: r.res.status === 201 || r.res.status === 200 ? "PASS" : "FAIL",
        httpStatus: r.res.status,
        detail: post?.id ?? JSON.stringify(r.json).slice(0, 180),
        severity: r.res.status >= 400 ? "high" : undefined,
      });
    } else {
      record({
        id: "MAN-03",
        feature: "Manual content validation",
        name: "Empty caption rejected",
        status: r.res.status === 400 ? "PASS" : "FAIL",
        httpStatus: r.res.status,
        detail: JSON.stringify(r.json).slice(0, 180),
      });
    }
  }

  {
    const r = await req("GET", `/api/generation/posts?projectId=${encodeURIComponent(projectId)}`);
    const posts = (r.json as { posts?: unknown[] } | null)?.posts;
    record({
      id: "GEN-01",
      feature: "Content library",
      name: "List posts for Gventure project",
      status: r.res.status === 200 && Array.isArray(posts) ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: Array.isArray(posts) ? `count=${posts.length}` : undefined,
    });
  }

  // Full generate → approve path (requires confirmed businessContext in snapshot)
  let generatedPostId: string | null = null;
  {
    const scoped = snapshot.dataByProjectId[projectId];
    const r = await req("POST", "/api/generation/generate", {
      body: {
        projectId,
        project: snapshot.projects[0],
        brand: {
          ...scoped.brand,
          contactPhone: "919911837377",
          contactEmail: "info@gventure.us",
          targetAudience: scoped.context.businessContext.audience,
          productsServices: GV.services.join(", "),
        },
        context: scoped.context,
        sequencePostId: "seq-1",
        connectedPlatforms: ["linkedin"],
        schedule: scoped.schedule,
        automation: scoped.automation,
        accounts: scoped.accounts,
        mode: "specific",
      },
    });
    const body = r.json as {
      post?: { id?: string; caption?: string; imageUrl?: string | null; status?: string };
      error?: string;
      code?: string;
    } | null;
    generatedPostId = body?.post?.id ?? null;
    const providerBlocked =
      r.res.status === 400 &&
      /api key|provider|credit|quota|timeout|CONTEXT_NOT_CONFIRMED|model:|anthropic|openai|invalid|unauthorized|authentication/i.test(
        `${body?.code ?? ""} ${body?.error ?? ""}`,
      );
    record({
      id: "GEN-02",
      feature: "AI generation",
      name: "Generate draft with confirmed business context",
      status:
        r.res.status === 200 && generatedPostId
          ? "PASS"
          : providerBlocked
            ? "BLOCKED"
            : "FAIL",
      httpStatus: r.res.status,
      ms: r.ms,
      detail: generatedPostId
        ? `post=${generatedPostId} captionLen=${body?.post?.caption?.length ?? 0} image=${Boolean(body?.post?.imageUrl)}`
        : `${body?.code ?? ""} ${body?.error ?? JSON.stringify(body)}`.trim().slice(0, 220),
      severity: providerBlocked ? undefined : "high",
    });
  }

  if (generatedPostId) {
    const r = await req("PATCH", `/api/generation/posts/${generatedPostId}`, {
      body: { status: "approved" },
    });
    const post = (r.json as { post?: { status?: string } } | null)?.post;
    record({
      id: "GEN-03",
      feature: "Post review",
      name: "Approve generated draft",
      status: r.res.status === 200 && post?.status === "approved" ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: post?.status ?? JSON.stringify(r.json).slice(0, 180),
    });
  } else if (manualPostId) {
    // Complete the review path even when AI generation is provider-blocked.
    const r = await req("PATCH", `/api/generation/posts/${manualPostId}`, {
      body: { status: "approved" },
    });
    const post = (r.json as { post?: { status?: string } } | null)?.post;
    record({
      id: "GEN-03",
      feature: "Post review",
      name: "Approve draft (manual fallback when AI blocked)",
      status: r.res.status === 200 && post?.status === "approved" ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: post?.status
        ? `${post.status} (manual ${manualPostId})`
        : JSON.stringify(r.json).slice(0, 180),
    });
  } else {
    record({
      id: "GEN-03",
      feature: "Post review",
      name: "Approve generated draft",
      status: "BLOCKED",
      detail: "Skipped — no generated or manual post id",
    });
  }

  // Confirm gate still rejects unconfirmed context
  {
    const scoped = snapshot.dataByProjectId[projectId];
    const r = await req("POST", "/api/generation/generate", {
      body: {
        projectId,
        project: snapshot.projects[0],
        brand: scoped.brand,
        context: {
          ...scoped.context,
          businessContext: {
            ...scoped.context.businessContext,
            confirmed: false,
          },
        },
        sequencePostId: "seq-1",
        connectedPlatforms: ["linkedin"],
        mode: "specific",
      },
    });
    const code = (r.json as { code?: string } | null)?.code;
    record({
      id: "GEN-04",
      feature: "Business context gate",
      name: "Generate without confirmed context → CONTEXT_NOT_CONFIRMED",
      status: r.res.status === 400 && code === "CONTEXT_NOT_CONFIRMED" ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: `${code ?? ""} ${JSON.stringify(r.json).slice(0, 160)}`.trim(),
    });
  }

  // Platforms status / social gates
  {
    const r = await req("GET", "/api/social/platforms-status");
    const platforms = (r.json as { platforms?: Array<{ platform: string; status: string }> } | null)
      ?.platforms;
    const hasFb = platforms?.some((p) => p.platform === "facebook");
    record({
      id: "SOC-01",
      feature: "Social status API",
      name: "GET /api/social/platforms-status",
      status: r.res.status === 200 && hasFb ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: platforms
        ? platforms.map((p) => `${p.platform}:${p.status}`).join(", ")
        : JSON.stringify(r.json).slice(0, 200),
    });
  }
  {
    const r = await req(
      "GET",
      `/api/social/linkedin/connect?projectId=${encodeURIComponent(projectId)}`,
    );
    // 403 missing creds/disabled, 302 redirect to LinkedIn if configured
    const ok = r.res.status === 403 || r.res.status === 302 || r.res.status === 307;
    record({
      id: "SOC-02",
      feature: "OAuth connect gate",
      name: "LinkedIn connect gated or redirects",
      status: ok ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail:
        r.res.status === 403
          ? JSON.stringify(r.json).slice(0, 200)
          : `redirect=${r.res.headers.get("location")?.slice(0, 80) ?? "n/a"}`,
    });
  }
  {
    const r = await req(
      "GET",
      `/api/projects/${encodeURIComponent(projectId)}/social-accounts`,
    );
    record({
      id: "SOC-03",
      feature: "Connected accounts API",
      name: "List social accounts for project",
      status: r.res.status === 200 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
    });
  }

  // Publish without connected accounts — expect structured failure, not 500
  if (manualPostId) {
    const r = await req("POST", `/api/generation/posts/${encodeURIComponent(manualPostId)}/publish`, {
      body: {
        forcePublish: true,
        quickPublish: true,
        projectTimezone: "Asia/Kolkata",
        projectCountries: ["IN"],
        postsPerDay: 2,
        scheduleSaved: false,
      },
    });
    const body = r.json as { code?: string; error?: string; outcomes?: unknown[] } | null;
    const expectedFail = r.res.status === 400 || r.res.status === 403;
    record({
      id: "PUB-01",
      feature: "Publish hub",
      name: "Publish without connected accounts fails safely",
      status: expectedFail && r.res.status !== 500 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: `${body?.code ?? ""} ${body?.error ?? ""}`.trim().slice(0, 220),
      severity: r.res.status === 500 ? "critical" : undefined,
    });
  } else {
    record({
      id: "PUB-01",
      feature: "Publish hub",
      name: "Publish without connected accounts",
      status: "BLOCKED",
      detail: "No manual post id from earlier step",
    });
  }

  // AI config / RAG (may be disabled)
  {
    const r = await req("GET", "/api/ai/config");
    record({
      id: "AI-01",
      feature: "AI config",
      name: "GET /api/ai/config",
      status: r.res.status === 200 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      detail: JSON.stringify(r.json).slice(0, 180),
    });
  }
  {
    const r = await req("POST", "/api/rag/query", {
      body: {
        question: "Best practices for LinkedIn posts about VoIP dialer ROI",
        platform: "linkedin",
      },
    });
    const ragOk =
      r.res.status === 200 ||
      r.res.status === 503 ||
      (r.res.status === 500 &&
        typeof (r.json as { error?: string })?.error === "string" &&
        /api key|provider|timeout|rate/i.test((r.json as { error?: string }).error ?? ""));
    record({
      id: "RAG-01",
      feature: "RAG",
      name: "RAG query for dialer LinkedIn content",
      status: r.res.status === 200 ? "PASS" : ragOk ? "BLOCKED" : "FAIL",
      httpStatus: r.res.status,
      ms: r.ms,
      detail: JSON.stringify(r.json).slice(0, 200),
    });
  }

  // Cross-user / IDOR soft check — random project id
  {
    const r = await req("POST", "/api/content/manual", {
      body: {
        projectId: "proj_someone_else",
        title: "Steal",
        caption: "Should 404",
        platforms: ["linkedin"],
      },
    });
    record({
      id: "SEC-05",
      feature: "Authorization",
      name: "Manual post for foreign projectId → 404",
      status: r.res.status === 404 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      severity: r.res.status === 201 ? "critical" : "high",
    });
  }

  // Page smoke (authed)
  for (const page of [
    "/dashboard",
    "/my-project",
    "/brand-profile",
    "/posting-context",
    "/connected-accounts",
    "/generated-posts",
    "/content-library",
    "/smart-schedule",
    "/live-generation",
    "/automation",
  ]) {
    const r = await req("GET", page);
    record({
      id: `UI-${page}`,
      feature: "App pages",
      name: `GET ${page}`,
      status: r.res.status === 200 ? "PASS" : "FAIL",
      httpStatus: r.res.status,
      ms: r.ms,
      severity: r.res.status >= 500 ? "high" : undefined,
    });
  }

  // Logo asset reachability (external — documentation only)
  {
    const started = Date.now();
    try {
      const res = await fetch(GV.logo, { method: "GET", redirect: "follow" });
      record({
        id: "ASSET-01",
        feature: "Brand assets",
        name: "Gventure logo URL reachable",
        status: res.ok ? "PASS" : "FAIL",
        httpStatus: res.status,
        ms: Date.now() - started,
      });
    } catch (e) {
      record({
        id: "ASSET-01",
        feature: "Brand assets",
        name: "Gventure logo URL reachable",
        status: "FAIL",
        detail: e instanceof Error ? e.message : "fetch failed",
      });
    }
  }

  finish();
}

function finish() {
  const pass = results.filter((r) => r.status === "PASS").length;
  const fail = results.filter((r) => r.status === "FAIL").length;
  const blocked = results.filter((r) => r.status === "BLOCKED").length;
  console.log(`\n=== Summary: ${pass} PASS / ${fail} FAIL / ${blocked} BLOCKED (total ${results.length}) ===\n`);

  const outDir = path.join(process.cwd(), "data", "qa");
  mkdirSync(outDir, { recursive: true });
  const outPath = path.join(outDir, `gventure-e2e-${Date.now()}.json`);
  writeFileSync(
    outPath,
    JSON.stringify(
      {
        base: BASE,
        company: GV.name,
        website: GV.website,
        ranAt: new Date().toISOString(),
        summary: { pass, fail, blocked, total: results.length },
        results,
      },
      null,
      2,
    ),
    "utf-8",
  );
  console.log(`Wrote ${outPath}`);
  if (fail > 0) process.exitCode = 1;
}

void main().catch((e) => {
  console.error(e);
  process.exit(1);
});
