import type { CricutLayer, CricutPanel, CricutProject } from "../types";

const safePanelWidth = 11.5;
const safePanelHeight = 11.5;
const cricutDisclaimer = "Cricut is a trademark of Cricut, Inc. ProjectCraft Pro is not affiliated with, sponsored by, authorized by, or endorsed by Cricut, Inc.";
const copyrightNotice = "© 2026 ProjectCraft Pro. All rights reserved.";

function svgNumber(value: number): string {
  return Number(value.toFixed(4)).toString();
}

function escapeXml(value: string): string {
  return value
    .replaceAll("&", "&amp;")
    .replaceAll("\"", "&quot;")
    .replaceAll("'", "&apos;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;");
}

function safeFileName(value: string): string {
  const match = value.trim().match(/^(.*?)(\.[a-z0-9]{2,8})$/i);
  const base = match?.[1] ?? value;
  const extension = match?.[2] ?? "";
  const safeBase = base
    .trim()
    .replace(/[^a-z0-9]+/gi, "-")
    .replace(/^-+|-+$/g, "")
    .slice(0, 80);

  return `${safeBase || "projectcraft-pro-export"}${extension.toLowerCase()}`;
}

function isNativeCapacitor(): boolean {
  return typeof window !== "undefined"
    && Boolean((window as { Capacitor?: { isNativePlatform?: () => boolean } }).Capacitor?.isNativePlatform?.());
}

type CraftLaunchFilesPlugin = {
  saveTextFile: (options: { fileName: string; contents: string; mimeType: string }) => Promise<{ uri?: string; path?: string }>;
};

function mimeTypeForFileName(fileName: string): string {
  if (fileName.toLowerCase().endsWith(".svg")) {
    return "image/svg+xml";
  }

  if (fileName.toLowerCase().endsWith(".txt")) {
    return "text/plain";
  }

  return "application/octet-stream";
}

async function saveWithCapacitor(fileName: string, contents: string): Promise<boolean> {
  if (!isNativeCapacitor()) {
    return false;
  }

  try {
    const [{ registerPlugin }, { Share }] = await Promise.all([
      import("@capacitor/core"),
      import("@capacitor/share")
    ]);
    const files = registerPlugin<CraftLaunchFilesPlugin>("CraftLaunchFiles");
    const result = await files.saveTextFile({
      fileName,
      contents,
      mimeType: mimeTypeForFileName(fileName)
    });
    const canShare = await Share.canShare();

    if (canShare.value && result.uri) {
      await Share.share({
        title: fileName,
        text: `Exported from ProjectCraft Pro: ${fileName}`,
        files: [result.uri],
        dialogTitle: "Share ProjectCraft export"
      });
    }

    return true;
  } catch (error) {
    console.warn("Native mobile export failed; falling back to mobile share text.", error);

    try {
      const { Share } = await import("@capacitor/share");
      const canShare = await Share.canShare();

      if (canShare.value) {
        await Share.share({
          title: fileName,
          text: contents,
          dialogTitle: "Share ProjectCraft export"
        });
        return true;
      }
    } catch (shareError) {
      console.warn("Mobile share fallback failed.", shareError);
    }

    return false;
  }
}

type FinishedSize = {
  width: number;
  height: number;
};

const colorHex: Record<string, string> = {
  black: "#111111",
  white: "#FFFFFF",
  red: "#D71920",
  blue: "#1F5EAE",
  green: "#16824A",
  yellow: "#F2C230",
  orange: "#E87722",
  purple: "#6F42C1",
  pink: "#D84C8B",
  gray: "#667085",
  grey: "#667085",
  brown: "#8A5A32",
  gold: "#C89B2C",
  silver: "#B8C0C8",
  clear: "#D9EEF7"
};

function cleanTitle(value: string): string {
  return value
    .replace(/^create\s+(?:a|an|the)?\s*/i, "")
    .replace(/^design\s+(?:a|an|the)?\s*/i, "")
    .replace(/^build\s+(?:a|an|the)?\s*/i, "")
    .replace(/[.:]+$/g, "")
    .trim();
}

function inferProjectTitle(prompt: string): string {
  const firstUsefulLine = prompt
    .split(/\r?\n/)
    .map((line) => line.trim())
    .find((line) => line.length > 0 && !/^(finished size|overall design|layout requirements|requirements|material|style|section\s+[a-z])/i.test(line));

  const title = cleanTitle(firstUsefulLine ?? "Custom Cricut Project");
  return (title || "Custom Cricut Project").slice(0, 72);
}

function clampDimension(value: number, fallback: number): number {
  if (!Number.isFinite(value) || value <= 0) {
    return fallback;
  }

  return Math.min(Math.max(value, 1), 144);
}

function inferFinishedSize(prompt: string): FinishedSize {
  const normalized = prompt.replace(/[×]/g, "x");
  const wideTall = normalized.match(/(\d+(?:\.\d+)?)\s*(?:inches|inch|in|\")?\s*(?:wide|w)\b[\s\S]{0,80}?(\d+(?:\.\d+)?)\s*(?:inches|inch|in|\")?\s*(?:tall|high|h)\b/i);
  if (wideTall) {
    return {
      width: clampDimension(Number(wideTall[1]), 12),
      height: clampDimension(Number(wideTall[2]), 12)
    };
  }

  const crossSize = normalized.match(/(\d+(?:\.\d+)?)\s*(?:inches|inch|in|\")?\s*x\s*(\d+(?:\.\d+)?)\s*(?:inches|inch|in|\")?/i);
  if (crossSize) {
    return {
      width: clampDimension(Number(crossSize[1]), 12),
      height: clampDimension(Number(crossSize[2]), 12)
    };
  }

  return { width: 12, height: 12 };
}

function colorNameForKey(color: string): string {
  return color === "grey" ? "Gray" : color.charAt(0).toUpperCase() + color.slice(1);
}

function colorForLayer(colorName: string): string {
  return colorHex[colorName.toLowerCase()] ?? "#111111";
}

function contrastColor(hex: string): string {
  const value = hex.replace("#", "");
  const r = parseInt(value.slice(0, 2), 16);
  const g = parseInt(value.slice(2, 4), 16);
  const b = parseInt(value.slice(4, 6), 16);
  const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
  return luminance > 0.6 ? "#111111" : "#FFFFFF";
}

function requestedColors(prompt: string): string[] {
  const normalized = prompt.toLowerCase();
  const colors = Object.keys(colorHex).filter((color) => new RegExp(`\\b${color}\\b`, "i").test(normalized));
  const unique = colors.filter((color, index) => colors.indexOf(color) === index && !(color === "grey" && colors.includes("gray")));
  return unique.length > 0 ? unique.slice(0, 8) : ["black"];
}

function inferLayers(prompt: string, size: FinishedSize): CricutLayer[] {
  const colors = requestedColors(prompt);
  const usableWidth = Math.max(size.width - 1, 1);
  const usableHeight = Math.max(size.height - 1, 1);
  const bandHeight = Math.max(1, usableHeight / colors.length);

  return colors.map((color, index) => {
    const colorName = colorNameForKey(color);
    const optionalPattern = new RegExp(`(optional[\\s\\S]{0,48}${color}|${color}[\\s\\S]{0,48}optional)`, "i");
    const layerHeight = Math.max(Math.min(bandHeight - 0.25, usableHeight), 1);

    return {
      id: `${color}-cut-layer-${index + 1}`,
      name: `${colorName} Cut Layer${optionalPattern.test(prompt) ? " - optional" : ""}`,
      colorName,
      material: `${colorName} vinyl, stencil vinyl, cardstock, acrylic, or confirmed craft material`,
      x: 0.5,
      y: 0.5 + index * bandHeight,
      width: usableWidth,
      height: layerHeight,
      optional: optionalPattern.test(prompt),
      notes: "Layer inferred from the pasted prompt. Confirm exact art, fonts, and material before final cutting."
    };
  });
}

function layerGroup(layer: CricutLayer): string {
  const groupId = safeFileName(layer.id).replace(/[^a-z0-9_-]+/gi, "_");
  const fill = colorForLayer(layer.colorName);
  const textFill = contrastColor(fill);
  const label = layer.name.replace(/\s+-\s+optional$/i, "");
  const textSize = Math.max(0.28, Math.min(layer.height * 0.34, layer.width / Math.max(label.length * 0.62, 1), 1.15));
  const labelY = layer.y + layer.height / 2 + textSize / 3;

  return `
  <g id="${escapeXml(groupId)}" data-name="${escapeXml(layer.name)}" data-material="${escapeXml(layer.material)}">
    <rect x="${svgNumber(layer.x)}" y="${svgNumber(layer.y)}" width="${svgNumber(layer.width)}" height="${svgNumber(layer.height)}" rx="0.08" fill="${fill}" opacity="${layer.optional ? "0.78" : "1"}" />
    <text x="${svgNumber(layer.x + layer.width / 2)}" y="${svgNumber(labelY)}" font-family="Arial Black, Helvetica Neue, Arial, sans-serif" font-size="${svgNumber(textSize)}" font-weight="900" text-anchor="middle" fill="${textFill}">${escapeXml(label)}</text>
  </g>`;
}

function registrationGroup(width: number, height: number): string {
  const mark = Math.max(0.25, Math.min(0.5, Math.min(width, height) / 18));
  const right = Math.max(width - 1 - mark, mark);
  const bottom = Math.max(height - 1 - mark, mark);

  return `
  <g id="registration_marks" data-name="Registration Marks" data-cut-role="alignment">
    <rect id="registration_top_left" x="1" y="1" width="${svgNumber(mark)}" height="${svgNumber(mark)}" fill="#111111" />
    <rect id="registration_top_right" x="${svgNumber(right)}" y="1" width="${svgNumber(mark)}" height="${svgNumber(mark)}" fill="#111111" />
    <rect id="registration_center" x="${svgNumber(width / 2 - mark / 2)}" y="${svgNumber(height / 2 - mark / 2)}" width="${svgNumber(mark)}" height="${svgNumber(mark)}" fill="#111111" />
    <rect id="registration_bottom_left" x="1" y="${svgNumber(bottom)}" width="${svgNumber(mark)}" height="${svgNumber(mark)}" fill="#111111" />
    <rect id="registration_bottom_right" x="${svgNumber(right)}" y="${svgNumber(bottom)}" width="${svgNumber(mark)}" height="${svgNumber(mark)}" fill="#111111" />
  </g>`;
}

function masterSvg(title: string, size: FinishedSize, layers: CricutLayer[]): string {
  return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${svgNumber(size.width)}in" height="${svgNumber(size.height)}in" viewBox="0 0 ${svgNumber(size.width)} ${svgNumber(size.height)}" data-grid-size="1in" data-cricut-mat="Maker 4" data-source="ProjectCraft Pro" data-copyright="© 2026 ProjectCraft Pro. All rights reserved.">
  <title>${escapeXml(title)}</title>
  <metadata>Layered clean cut-template SVG generated from a pasted project prompt. No photos, textures, filters, scripts, raster images, backgrounds, or lighting effects.</metadata>
${registrationGroup(size.width, size.height)}
${layers.map(layerGroup).join("\n")}
</svg>
`;
}

function panelSvg(projectTitle: string, layer: CricutLayer, panel: Omit<CricutPanel, "svg">): string {
  return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${svgNumber(panel.width)}in" height="${svgNumber(panel.height)}in" viewBox="${svgNumber(panel.x)} ${svgNumber(panel.y)} ${svgNumber(panel.width)} ${svgNumber(panel.height)}" data-source="ProjectCraft Pro tiled panel" data-copyright="© 2026 ProjectCraft Pro. All rights reserved.">
  <title>${escapeXml(projectTitle)} - ${escapeXml(panel.id)}</title>
  <metadata>Tile panel ${escapeXml(panel.id)}. Original origin: x ${svgNumber(panel.x)} in, y ${svgNumber(panel.y)} in.</metadata>
${layerGroup(layer)}
</svg>
`;
}

function panelsForLayer(title: string, layer: CricutLayer): CricutPanel[] {
  const columns = Math.ceil(layer.width / safePanelWidth);
  const rows = Math.ceil(layer.height / safePanelHeight);
  const panels: CricutPanel[] = [];

  for (let column = 1; column <= columns; column += 1) {
    for (let row = 1; row <= rows; row += 1) {
      const width = Math.min(safePanelWidth, layer.width - (column - 1) * safePanelWidth);
      const height = Math.min(safePanelHeight, layer.height - (row - 1) * safePanelHeight);
      const panelBase = {
        id: `${layer.id}-C${String(column).padStart(2, "0")}-R${String(row).padStart(2, "0")}`,
        layerName: layer.name,
        column,
        row,
        x: layer.x + (column - 1) * safePanelWidth,
        y: layer.y + (row - 1) * safePanelHeight,
        width,
        height
      };

      panels.push({
        ...panelBase,
        svg: panelSvg(title, layer, panelBase)
      });
    }
  }

  return panels;
}

export function buildGenericProject(prompt: string): CricutProject {
  const title = inferProjectTitle(prompt);
  const size = inferFinishedSize(prompt);
  const layers = inferLayers(prompt, size);
  const panels = layers.flatMap((layer) => panelsForLayer(title, layer));
  const layerSummary = layers
    .map((layer) => `- ${layer.name}: ${svgNumber(layer.width)} in x ${svgNumber(layer.height)} in, ${layer.material}.`)
    .join("\n");

  return {
    title,
    width: size.width,
    height: size.height,
    prompt,
    projectPlan: `Project Plan
Title: ${title}
Finished size: ${svgNumber(size.width)} inches x ${svgNumber(size.height)} inches
Grid: 1-inch layout grid
Output: layered SVG-style Cricut cut template

Inferred cut layers:
${layerSummary}

Cricut setup:
- Cut each color or material as a separate layer.
- Use tiled panels when a layer exceeds the selected mat size.
- Keep registration squares until installation alignment is complete.
- Review the generated geometry before production cutting.`,
    assemblyGuide: `Assembly Guide
1. Confirm the final bounding size and material choice.
2. Cut one material/color layer at a time.
3. Keep registration marks attached until adjacent tiles line up.
4. Test-fit panels before applying permanent vinyl, paint, or adhesive.
5. Remove registration marks only after the full layout is aligned.`,
    layers,
    panels,
    svg: masterSvg(title, size, layers)
  };
}

export function buildProjectFromPrompt(prompt: string): CricutProject {
  return buildGenericProject(prompt);
}

export function downloadTextFile(fileName: string, contents: string, mimeType = "text/plain"): void {
  const blob = new Blob([contents], { type: mimeType });
  const url = URL.createObjectURL(blob);
  const safeName = safeFileName(fileName);

  void saveWithCapacitor(safeName, contents).then((saved) => {
    if (saved) {
      URL.revokeObjectURL(url);
    }
  });

  if (isNativeCapacitor()) {
    return;
  }

  if (typeof chrome !== "undefined" && chrome.downloads?.download) {
    chrome.downloads.download({ url, filename: safeName, saveAs: false }, () => {
      window.setTimeout(() => URL.revokeObjectURL(url), 1000);
    });
    return;
  }

  const link = document.createElement("a");
  link.href = url;
  link.download = safeName;
  document.body.appendChild(link);
  link.click();
  link.remove();
  URL.revokeObjectURL(url);
}

export function handoffManifest(project: CricutProject): string {
  const panelList = project.panels
    .map((panel) => `- ${panel.id}.svg: ${panel.layerName}, column ${panel.column}, row ${panel.row}, ${svgNumber(panel.width)} in x ${svgNumber(panel.height)} in`)
    .join("\n");

  return `Cricut Handoff
Project: ${project.title}
Finished size: ${svgNumber(project.width)} in x ${svgNumber(project.height)} in
Generated by ProjectCraft Pro.
${copyrightNotice}

${cricutDisclaimer}

Recommended workflow:
1. Upload the master SVG only as a reference.
2. For cutting the real wall sign, upload tiled panel SVGs one at a time by layer.
3. Use On Mat and 12 in x 12 in material size.
4. Keep registration marks until installation is aligned.

Panel files:
${panelList}
`;
}
