import { chromium } from "playwright-core";
import { Writable } from "stream";
import archiver from "archiver";
import sharp from "sharp";
import { puterChatComplete } from "../puter-inference.js";
import { resolveBrowserPath } from "./diagram-renderer.js";
import { validateAndCleanSvg } from "./icon-renderer.js";
import type { RenderResult, BrandKitBundle } from "./types.js";

// ─── Brand specification ────────────────────────────────────────────────────────

interface BrandSpec {
  name: string;
  tagline: string;
  primaryColor: string;
  secondaryColor: string;
  fontStyle: string;
  industry: string;
  logoDescription: string;
}

// ─── Social platform dimensions ─────────────────────────────────────────────────

const SOCIAL_DIMENSIONS: Record<string, { width: number; height: number }> = {
  "twitter-profile": { width: 400, height: 400 },
  "twitter-banner": { width: 1500, height: 500 },
  "linkedin-profile": { width: 400, height: 400 },
  "linkedin-banner": { width: 1584, height: 396 },
  "instagram-profile": { width: 1080, height: 1080 },
};

// ─── Strip markdown fences ──────────────────────────────────────────────────────

function stripMarkdownFences(raw: string): string {
  return raw
    .trim()
    .replace(/^```(?:json|svg|xml|html)?\s*/i, "")
    .replace(/\s*```$/, "")
    .trim();
}

// ─── Step 1: Extract brand spec from prompt ─────────────────────────────────────

async function extractBrandSpec(prompt: string): Promise<BrandSpec> {
  const systemPrompt = [
    "You are a brand identity strategist.",
    "Extract or invent a brand specification from the user's prompt.",
    "Respond with ONLY a JSON object — no markdown fences, no explanation.",
    "Required fields:",
    '  "name": brand name (string)',
    '  "tagline": short catchy tagline (string)',
    '  "primaryColor": primary brand color as hex (e.g. "#FF5733")',
    '  "secondaryColor": secondary brand color as hex (e.g. "#33C1FF")',
    '  "fontStyle": one of "sans", "serif", or "mono"',
    '  "industry": industry sector (string)',
    '  "logoDescription": brief description of a simple geometric logo mark (string)',
  ].join("\n");

  const raw = await puterChatComplete([
    { role: "system", content: systemPrompt },
    { role: "user", content: prompt },
  ]);

  try {
    const cleaned = stripMarkdownFences(raw);
    const parsed = JSON.parse(cleaned) as Partial<BrandSpec>;
    return {
      name: parsed.name ?? "Brand",
      tagline: parsed.tagline ?? "Building the future",
      primaryColor: parsed.primaryColor ?? "#3B82F6",
      secondaryColor: parsed.secondaryColor ?? "#10B981",
      fontStyle: parsed.fontStyle ?? "sans",
      industry: parsed.industry ?? "technology",
      logoDescription: parsed.logoDescription ?? "A simple geometric mark",
    };
  } catch {
    // Fallback defaults if parsing fails
    return {
      name: "Brand",
      tagline: "Building the future",
      primaryColor: "#3B82F6",
      secondaryColor: "#10B981",
      fontStyle: "sans",
      industry: "technology",
      logoDescription: "A simple geometric mark",
    };
  }
}

// ─── Step 2: Generate logo SVG via LLM ─────────────────────────────────────────

async function generateLogoSvg(spec: BrandSpec): Promise<string> {
  const systemPrompt = [
    "You are an SVG logo designer.",
    "Output ONLY valid SVG. No text explanations. No markdown fences.",
    "Simple geometric shapes only. viewBox 0 0 512 512.",
    `Use only these colors: primary=${spec.primaryColor}, secondary=${spec.secondaryColor}`,
    "The SVG must start with <svg and end with </svg>.",
    "No text elements, no external resources, no scripts.",
  ].join("\n");

  const raw = await puterChatComplete([
    { role: "system", content: systemPrompt },
    { role: "user", content: `Create a logo mark for: ${spec.logoDescription}` },
  ]);

  const cleaned = stripMarkdownFences(raw);
  const { svg } = validateAndCleanSvg(cleaned);
  return svg;
}

// ─── Step 3: Rasterize logo to avatar sizes ─────────────────────────────────────

async function rasterizeAvatars(logoSvg: string): Promise<Record<string, string>> {
  const sizes = [512, 256, 128, 64, 32];
  const avatarPngs: Record<string, string> = {};

  for (const size of sizes) {
    const pngBuffer = await sharp(Buffer.from(logoSvg))
      .resize(size, size)
      .png()
      .toBuffer();
    avatarPngs[String(size)] = pngBuffer.toString("base64");
  }

  return avatarPngs;
}

// ─── Step 4: Generate social platform images ────────────────────────────────────

async function generateSocialImages(
  spec: BrandSpec,
  logoSvg: string,
): Promise<Record<string, string>> {
  const socialImages: Record<string, string> = {};

  for (const [platform, dims] of Object.entries(SOCIAL_DIMENSIONS)) {
    const { width, height } = dims;

    // Build SVG template: colored background + centered logo
    const logoScale = Math.min(width, height) * 0.5;
    const logoX = (width - logoScale) / 2;
    const logoY = (height - logoScale) / 2;

    const socialSvg = [
      `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">`,
      `  <rect width="${width}" height="${height}" fill="${spec.primaryColor}"/>`,
      `  <image x="${logoX}" y="${logoY}" width="${logoScale}" height="${logoScale}" href="data:image/svg+xml;base64,${Buffer.from(logoSvg).toString("base64")}"/>`,
      `</svg>`,
    ].join("\n");

    const pngBuffer = await sharp(Buffer.from(socialSvg), { density: 72 })
      .resize(width, height, { fit: "fill" })
      .png({ compressionLevel: 9 })
      .toBuffer();

    socialImages[platform] = pngBuffer.toString("base64");
  }

  return socialImages;
}

// ─── Step 5: Generate email signature and letterhead HTML ───────────────────────

async function generateTemplates(
  spec: BrandSpec,
): Promise<{ signature: string; letterhead: string }> {
  const signatureSystemPrompt = [
    "You are a professional HTML email signature designer.",
    "Generate a professional HTML email signature with name, tagline, colors.",
    "Inline CSS only. No external resources. Keep under 200 lines.",
    "Output ONLY the HTML — no markdown fences, no explanation.",
    `Brand: name="${spec.name}", tagline="${spec.tagline}", primary=${spec.primaryColor}, secondary=${spec.secondaryColor}`,
  ].join("\n");

  const letterheadSystemPrompt = [
    "You are a professional HTML document designer.",
    "Generate a professional HTML letterhead template with header, footer, placeholder body.",
    "Inline CSS only. No external resources.",
    "Output ONLY the HTML — no markdown fences, no explanation.",
    `Brand: name="${spec.name}", tagline="${spec.tagline}", primary=${spec.primaryColor}, secondary=${spec.secondaryColor}`,
  ].join("\n");

  const [rawSignature, rawLetterhead] = await Promise.all([
    puterChatComplete([
      { role: "system", content: signatureSystemPrompt },
      { role: "user", content: `Create an email signature for ${spec.name}` },
    ]),
    puterChatComplete([
      { role: "system", content: letterheadSystemPrompt },
      { role: "user", content: `Create a letterhead template for ${spec.name}` },
    ]),
  ]);

  return {
    signature: stripMarkdownFences(rawSignature),
    letterhead: stripMarkdownFences(rawLetterhead),
  };
}

// ─── Step 6: Generate brand guidelines PDF via Playwright ───────────────────────

async function generateGuidelinesPdf(
  spec: BrandSpec,
  browser: Awaited<ReturnType<typeof chromium.launch>>,
): Promise<Buffer> {
  const guidelinesSystemPrompt = [
    "You are a professional brand designer.",
    "Generate a brand guidelines document as HTML.",
    "Include: brand name, tagline, color palette with hex codes, typography guidance, logo usage rules, spacing guidelines.",
    "Inline CSS only. No external resources. Use web-safe fonts.",
    "Output ONLY a complete HTML document starting with <!DOCTYPE html> — no markdown fences, no explanation.",
    `Brand: name="${spec.name}", tagline="${spec.tagline}", primary=${spec.primaryColor}, secondary=${spec.secondaryColor}, fontStyle=${spec.fontStyle}, industry=${spec.industry}`,
  ].join("\n");

  const rawHtml = await puterChatComplete([
    { role: "system", content: guidelinesSystemPrompt },
    { role: "user", content: `Create brand guidelines for ${spec.name}` },
  ]);

  const cleanHtml = stripMarkdownFences(rawHtml);

  const page = await browser.newPage();
  const pdfUint8 = await (async () => {
    await page.setContent(cleanHtml, { waitUntil: "domcontentloaded" });
    return page.pdf({
      format: "A4",
      printBackground: true,
      margin: { top: "20mm", bottom: "20mm", left: "20mm", right: "20mm" },
    });
  })();

  return Buffer.from(pdfUint8);
}

// ─── Step 7: Package all assets into a ZIP buffer ──────────────────────────────

interface ZipAssets {
  logoSvg: string;
  avatarPngs: Record<string, string>;
  socialImages: Record<string, string>;
  signature: string;
  letterhead: string;
  guidelinesPdf: Buffer;
}

async function buildZip(assets: ZipAssets): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    const sink = new Writable({
      write(chunk: Buffer, _enc, cb) {
        chunks.push(chunk);
        cb();
      },
    });

    const archive = archiver("zip", { zlib: { level: 6 } });
    archive.on("error", reject);
    sink.on("finish", () => resolve(Buffer.concat(chunks)));
    archive.pipe(sink);

    // Logo SVG
    archive.append(Buffer.from(assets.logoSvg), { name: "brand-kit/logo/logo.svg" });

    // Avatar PNGs
    for (const size of ["512", "256", "128", "64", "32"]) {
      const data = assets.avatarPngs[size];
      if (data) {
        archive.append(Buffer.from(data, "base64"), {
          name: `brand-kit/logo/logo-${size}.png`,
        });
      }
    }

    // Social images
    for (const [platform, data] of Object.entries(assets.socialImages)) {
      if (data) {
        archive.append(Buffer.from(data, "base64"), {
          name: `brand-kit/social/${platform}.png`,
        });
      }
    }

    // Templates
    archive.append(Buffer.from(assets.signature), {
      name: "brand-kit/templates/email-signature.html",
    });
    archive.append(Buffer.from(assets.letterhead), {
      name: "brand-kit/templates/letterhead.html",
    });

    // Guidelines PDF
    archive.append(assets.guidelinesPdf, { name: "brand-kit/guidelines.pdf" });

    void archive.finalize();
  });
}

// ─── Main exported renderer ─────────────────────────────────────────────────────

/**
 * Render a full brand identity kit from a single conversation prompt.
 *
 * Orchestrates: spec extraction → logo SVG → avatar rasterization →
 * social images → email signature + letterhead → guidelines PDF → ZIP package.
 *
 * Returns a RenderResult containing a BrandKitBundle JSON bundle.
 */
export async function renderBrandKit(
  input: Record<string, unknown>,
): Promise<RenderResult> {
  const prompt =
    typeof input.prompt === "string" ? input.prompt : "Create a brand identity";

  // Step 1: Extract brand spec from prompt
  const spec = await extractBrandSpec(prompt);

  // Step 2: Generate logo SVG via LLM
  const logoSvg = await generateLogoSvg(spec);

  // Step 3: Rasterize logo to 5 avatar sizes
  const avatarPngs = await rasterizeAvatars(logoSvg);

  // Step 4: Generate social platform images (colored background + centered logo)
  const socialImages = await generateSocialImages(spec, logoSvg);

  // Step 5: Generate email signature and letterhead HTML
  const { signature, letterhead } = await generateTemplates(spec);

  // Step 6: Open Playwright browser ONCE for the entire brand kit job
  const executablePath = resolveBrowserPath();
  const browser = await chromium.launch({
    executablePath,
    headless: true,
    args: ["--no-sandbox", "--disable-setuid-sandbox"],
  });

  let guidelinesPdf: Buffer;
  try {
    guidelinesPdf = await generateGuidelinesPdf(spec, browser);
  } finally {
    await browser.close();
  }

  // Step 7: Package everything into a ZIP
  const zipBuffer = await buildZip({
    logoSvg,
    avatarPngs,
    socialImages,
    signature,
    letterhead,
    guidelinesPdf,
  });

  // Assemble the BrandKitBundle
  const bundle: BrandKitBundle = {
    type: "brand-kit-bundle",
    spec: {
      name: spec.name,
      tagline: spec.tagline,
      primaryColor: spec.primaryColor,
      secondaryColor: spec.secondaryColor,
      fontStyle: spec.fontStyle,
      industry: spec.industry,
    },
    logoSvgBase64: Buffer.from(logoSvg).toString("base64"),
    avatarPngs,
    socialImages,
    signatureHtml: signature,
    letterheadHtml: letterhead,
    guidelinesPdfBase64: guidelinesPdf.toString("base64"),
    zipBase64: zipBuffer.toString("base64"),
  };

  return {
    filename: "brand-kit-bundle.json",
    contentType: "application/json",
    buffer: Buffer.from(JSON.stringify(bundle)),
  };
}
