import type { CanvasState } from "@dada/shared-contracts";
import { P0A_COMPLEX_RELEASE_VERSION, P0A_REQUIRED_FONT_PANEL_IDS, P0A_TEXT_TEMPLATE_IDS } from "@dada/template-registry";

import type { CanvasElementIdentity } from "./editor-elements.js";

type CanvasElement = CanvasState["elements"][number];

export type TextTemplateCategory = "flower" | "simple" | "tag" | "title";
export type TextAlign = "center" | "left" | "right";

export interface TextTemplateDefinition {
  available: boolean;
  catalogOrder: number;
  category: TextTemplateCategory;
  defaultFontId: string;
  defaultFontSize: number;
  defaultText: string;
  displayName: string;
  fontUrl?: string;
  resourceClass: "parameter_only" | "zip_template";
  resourceVersion: string;
  templateId: string;
}

export interface FontOption {
  displayName: string;
  fontId: string;
  url: string;
}

export interface TextStylePatch {
  backgroundColor?: string;
  backgroundEnabled?: boolean;
  backgroundOpacity?: number;
  fillColor?: string;
  fontOverride?: string | null;
  letterSpacing?: number;
  lineHeight?: number;
  strokeColor?: string;
  strokeEnabled?: boolean;
  strokeWidth?: number;
  textAlign?: TextAlign;
}

const resourceVersion = P0A_COMPLEX_RELEASE_VERSION;
const defaults = {
  background_color: "#FFE62C",
  background_enabled: false,
  background_opacity: 1,
  fill_color: "#111111",
  letter_spacing: 1,
  line_height: 1.2,
  stroke_color: "#000000",
  stroke_enabled: false,
  stroke_width: 0,
  text_align: "center",
} as const;

type CatalogSeed = [id: string, category: TextTemplateCategory, displayName: string, defaultText: string, defaultFontId: string, available?: boolean, resourceClass?: "parameter_only"];

const seeds: readonly CatalogSeed[] = [
  ["FLOWER001", "flower", "春日计划", "春日计划", "FONT011", true],
  ["FLOWER002", "flower", "笑不活了", "笑不活了", "FLOWER002_FONT"],
  ["FLOWER003", "flower", "人生照片", "人生照片", "FONT008"],
  ["FLOWER004", "flower", "我的日常生活", "我的日常生活", "FLOWER004_FONT"],
  ["FLOWER005", "flower", "碎片生活", "碎片生活", "FONT008"],
  ["FLOWER006", "flower", "闪光瞬间", "闪光瞬间", "FLOWER006_FONT"],
  ["FLOWER007", "flower", "好柿花生", "好柿花生", "FONT046", false, "parameter_only"],
  ["FLOWER008", "flower", "Vlog.", "Vlog.", "FONT005"],
  ["H001", "title", "电影生活记录", "电影生活记录", "H001_FONT"],
  ["H002", "title", "30°C", "30°C", "H002_FONT"],
  ["H003", "title", "生活分享家", "生活分享家", "FONT039", true],
  ["H004", "title", "快乐充值成功", "快乐充值成功", "FONT046"],
  ["H005", "title", "日常的镜头", "日常的镜头", "H005_FONT"],
  ["H006", "title", "慢生活指南", "慢生活指南", "FONT052"],
  ["H007", "title", "做个有闲人", "做个有闲人", "H007_FONT"],
  ["H008", "title", "海滩日记", "海滩日记", "H008_FONT"],
  ["TAG001", "tag", "自定义标签", "自定义标签", "FONT027"],
  ["TAG002", "tag", "自定义标签", "自定义标签", "FONT043"],
  ["TAG003", "tag", "打卡x1", "打卡x1", "FONT043"],
  ["TAG004", "tag", "自定义标签", "自定义标签", "TAG004_FONT"],
  ["TAG005", "tag", "自定义标签", "自定义标签", "FONT008"],
  ["TAG006", "tag", "City Walk", "City Walk", "TAG006_FONT"],
  ["TAG007", "tag", "打卡x1", "打卡x1", "FONT043"],
  ["TAG051", "tag", "自定义标签", "自定义标签", "FONT022"],
  ["SIMPLE001", "simple", "碎片回忆录", "碎片回忆录", "SIMPLE001_FONT"],
  ["SIMPLE002", "simple", "秋天的信笺", "秋天的信笺", "SIMPLE002_FONT"],
  ["SIMPLE003", "simple", "返航时海鸟追着船盘旋", "返航时海鸟追着船盘旋", "SIMPLE003_FONT"],
  ["SIMPLE004", "simple", "下段旅程，幸福丰盛。", "下段旅程，幸福丰盛。", "SIMPLE002_FONT"],
  ["SIMPLE005", "simple", "见信好。", "见信好。", "SIMPLE005_FONT"],
  ["SIMPLE006", "simple", "万物回春", "万物回春", "SIMPLE001_FONT"],
  ["SIMPLE007", "simple", "周而复始。", "周而复始。", "SIMPLE007_FONT"],
  ["SIMPLE008", "simple", "周五愉快", "周五愉快", "SIMPLE008_FONT"],
];

const seedById = new Map(seeds.map((seed) => [seed[0], seed]));

export const P0A_TEXT_TEMPLATES: readonly TextTemplateDefinition[] = P0A_TEXT_TEMPLATE_IDS.map((templateId, catalogOrder) => {
  const seed = seedById.get(templateId);
  if (!seed) throw new Error(`missing text template definition ${templateId}`);
  return {
    available: seed[5] === true,
    catalogOrder,
    category: seed[1],
    defaultFontId: seed[4],
    defaultFontSize: 48,
    defaultText: seed[3],
    displayName: seed[2],
    ...(seed[5] === true ? { fontUrl: `/api/v1/assets/public/${resourceVersion}/${seed[4]}` } : {}),
    resourceClass: seed[6] ?? "zip_template",
    resourceVersion,
    templateId,
  };
});

const fontOptionDefinitions: Readonly<Record<typeof P0A_REQUIRED_FONT_PANEL_IDS[number], string>> = {
  FONT005: "Rammetto",
  FONT008: "正圆体",
  FONT011: "默陌手写",
  FONT021: "喜月体",
  FONT022: "素白体",
  FONT027: "锐正圆",
  FONT039: "字由油漆",
  FONT043: "喜脉体",
  FONT046: "可口可乐",
  FONT052: "Oraqle Script",
  FONT081: "Lexend Deca",
};

export const P0A_FONT_OPTIONS: readonly FontOption[] = P0A_REQUIRED_FONT_PANEL_IDS.map((fontId) => ({
  displayName: fontOptionDefinitions[fontId],
  fontId,
  url: `/api/v1/assets/public/${resourceVersion}/${fontId}`,
}));

export function fontOption(fontId: string) {
  return P0A_FONT_OPTIONS.find((option) => option.fontId === fontId);
}

export function fontIdForTextElement(element: CanvasElement) {
  const defaultFontId = element.style_parameters?.default_font_id;
  return element.font_override ?? (typeof defaultFontId === "string" ? defaultFontId : undefined);
}

function cloneElement(element: CanvasElement): CanvasElement {
  return structuredClone(element);
}

function templateById(templates: readonly TextTemplateDefinition[], templateId: string) {
  const template = templates.find((candidate) => candidate.templateId === templateId);
  if (!template) throw new Error("text_template_not_found");
  return template;
}

function checkedColor(value: string) {
  if (!/^#[0-9A-F]{6}$/i.test(value)) throw new Error("text_color_invalid");
  return value.toUpperCase();
}

function isStep(value: number, minimum: number, step: number) {
  return Math.abs((value - minimum) / step - Math.round((value - minimum) / step)) < 1e-8;
}

function templateStyle(template: TextTemplateDefinition): Record<string, string | number | boolean | null> {
  return { ...defaults, default_font_id: template.defaultFontId };
}

export function searchTextTemplates(
  templates: readonly TextTemplateDefinition[],
  input: { category?: TextTemplateCategory; query?: string },
) {
  const query = input.query?.trim().toLocaleLowerCase("zh-CN") ?? "";
  return templates
    .filter((template) => !input.category || template.category === input.category)
    .filter((template) => !query || template.displayName.toLocaleLowerCase("zh-CN").includes(query))
    .toSorted((left, right) => left.catalogOrder - right.catalogOrder);
}

export function createTextTemplateElement(
  template: TextTemplateDefinition,
  identity: CanvasElementIdentity,
  zIndex: number,
  transform: Partial<Pick<CanvasElement, "position" | "rotation" | "scale">> = {},
): CanvasElement {
  if (!template.available || !template.fontUrl) throw new Error("text_template_unavailable");
  return {
    content: template.defaultText,
    created_at: identity.createdAt,
    element_id: identity.elementId,
    font_size: template.defaultFontSize,
    opacity: 1,
    position: transform.position ?? { x: 0.5, y: 0.5 },
    resource_version: template.resourceVersion,
    rotation: transform.rotation ?? 0,
    scale: transform.scale ?? { x: 1, y: 1 },
    style_parameters: templateStyle(template),
    template_or_asset_id: template.templateId,
    type: "text_template",
    z_index: zIndex,
  };
}

export function effectiveFontSize(element: CanvasElement) {
  return (element.font_size ?? 48) * element.scale.x;
}

export class TextEditSession {
  readonly original: CanvasElement;
  private draft: CanvasElement;
  private readonly templates: readonly TextTemplateDefinition[];

  constructor(element: CanvasElement, templates: readonly TextTemplateDefinition[]) {
    if (element.type !== "text_template") throw new Error("text_element_required");
    this.original = cloneElement(element);
    this.draft = cloneElement(element);
    this.templates = templates;
  }

  get value() { return cloneElement(this.draft); }

  cancel() { return cloneElement(this.original); }

  complete() {
    if (!(this.draft.content ?? "").trim()) throw new Error("text_content_required");
    return cloneElement(this.draft);
  }

  setContent(content: string) {
    this.draft.content = content;
  }

  setEffectiveFontSize(value: number) {
    if (!Number.isFinite(value) || value < 1 || value > 512) throw new Error("text_font_size_invalid");
    const base = this.draft.font_size ?? 48;
    const factor = value / base;
    this.draft.scale = { x: factor, y: factor };
  }

  setStyle(patch: TextStylePatch) {
    const style = { ...(this.draft.style_parameters ?? {}) };
    if (patch.fillColor !== undefined) style.fill_color = checkedColor(patch.fillColor);
    if (patch.strokeColor !== undefined) style.stroke_color = checkedColor(patch.strokeColor);
    if (patch.backgroundColor !== undefined) style.background_color = checkedColor(patch.backgroundColor);
    if (patch.strokeEnabled !== undefined) style.stroke_enabled = patch.strokeEnabled;
    if (patch.backgroundEnabled !== undefined) style.background_enabled = patch.backgroundEnabled;
    if (patch.strokeWidth !== undefined) {
      if (!Number.isFinite(patch.strokeWidth) || patch.strokeWidth < 0 || patch.strokeWidth > 12) throw new Error("text_stroke_width_invalid");
      style.stroke_width = patch.strokeWidth;
    }
    if (patch.backgroundOpacity !== undefined) {
      if (!Number.isFinite(patch.backgroundOpacity) || patch.backgroundOpacity < 0 || patch.backgroundOpacity > 1) throw new Error("text_background_opacity_invalid");
      style.background_opacity = patch.backgroundOpacity;
    }
    if (patch.textAlign !== undefined) {
      if (!["center", "left", "right"].includes(patch.textAlign)) throw new Error("text_align_invalid");
      style.text_align = patch.textAlign;
    }
    if (patch.lineHeight !== undefined) {
      if (patch.lineHeight < 1 || patch.lineHeight > 1.9 || !isStep(patch.lineHeight, 1, 0.1)) throw new Error("text_line_height_invalid");
      style.line_height = patch.lineHeight;
    }
    if (patch.letterSpacing !== undefined) {
      if (patch.letterSpacing < 1 || patch.letterSpacing > 20 || !isStep(patch.letterSpacing, 1, 1)) throw new Error("text_letter_spacing_invalid");
      style.letter_spacing = patch.letterSpacing;
    }
    if (patch.fontOverride !== undefined) {
      if (patch.fontOverride === null) delete this.draft.font_override;
      else this.draft.font_override = patch.fontOverride;
    }
    this.draft.style_parameters = style;
  }

  switchTemplate(templateId: string) {
    const template = templateById(this.templates, templateId);
    if (!template.available || !template.fontUrl) throw new Error("text_template_unavailable");
    const content = this.draft.content;
    this.draft = {
      ...this.draft,
      font_size: template.defaultFontSize,
      resource_version: template.resourceVersion,
      style_parameters: templateStyle(template),
      template_or_asset_id: template.templateId,
    };
    if (content !== undefined) this.draft.content = content;
    delete this.draft.font_override;
  }
}
