import { api } from "../../../services/api";
import { renderPagination } from "../../../components/pagination";
import { getCurrentUser } from "../../../services/authState";
import type { DictionaryResponse, ModelItem, ModelListResponse } from "../../../types";
import { confirmDialog, formDialog, notify, notifyError } from "../../../ui/dialogs";
import { escapeHtml, modelNameFromFile } from "../../../utils/format";
import { appState } from "../appState";

type UploadFormState = {
  file: File | null;
};

type UploadModelPayload = {
  file: File;
  name: string;
  fileName?: string;
  folderId?: number;
  brandName?: string;
  typeName?: string;
  operationTree?: string;
  properties?: Record<string, string>;
};

const defaultOperationTree = JSON.stringify({ OperationTree: "[]" });
let dictionariesLoaded = false;
let dictionariesLoading: Promise<void> | null = null;

export function bindModelActions() {
  const isAdmin = getCurrentUser()?.role === "admin";
  if (isAdmin) {
    document.querySelector("#manageUsersBtn")?.addEventListener("click", async () => {
      try {
        const { openUserManager } = await import("./users");
        await openUserManager();
      } catch (error) {
        notifyError(error);
      }
    });

    document.querySelector("#manageDictionariesBtn")?.addEventListener("click", async () => {
      try {
        const { openDictionaryManager } = await import("./dictionaries");
        await openDictionaryManager(async () => {
          invalidateDictionaries();
          await loadModels();
        });
      } catch (error) {
        notifyError(error);
      }
    });

  }

  document.querySelector("#addModelBtn")?.addEventListener("click", async () => {
    try {
      await openUploadModelDialog();
    } catch (error) {
      notifyError(error);
    }
  });

  document.querySelector("#addProcessModelBtn")?.addEventListener("click", async () => {
    try {
      await openProcessModelUploadDialog();
    } catch (error) {
      notifyError(error);
    }
  });

  document.querySelector<HTMLSelectElement>("#brandFilter")!.addEventListener("change", async (event) => {
    appState.filters.brandId = (event.currentTarget as HTMLSelectElement).value;
    appState.page = 1;
    await loadModels();
  });
  document.querySelector<HTMLSelectElement>("#typeFilter")!.addEventListener("change", async (event) => {
    appState.filters.typeId = (event.currentTarget as HTMLSelectElement).value;
    appState.page = 1;
    await loadModels();
  });
  document.querySelector<HTMLInputElement>("#keywordFilter")!.addEventListener("keydown", async (event) => {
    if (event.key !== "Enter") return;
    appState.filters.keyword = (event.currentTarget as HTMLInputElement).value.trim();
    appState.page = 1;
    await loadModels();
  });
  document.querySelector<HTMLInputElement>("#keywordFilter")!.addEventListener("change", async (event) => {
    appState.filters.keyword = (event.currentTarget as HTMLInputElement).value.trim();
    appState.page = 1;
    await loadModels();
  });
  document.querySelector("#resetFilterBtn")!.addEventListener("click", async () => {
    appState.filters.brandId = "";
    appState.filters.typeId = "";
    appState.filters.keyword = "";
    document.querySelector<HTMLSelectElement>("#brandFilter")!.value = "";
    document.querySelector<HTMLSelectElement>("#typeFilter")!.value = "";
    document.querySelector<HTMLInputElement>("#keywordFilter")!.value = "";
    appState.page = 1;
    await loadModels();
  });
}

export async function loadModels() {
  const grid = document.querySelector<HTMLDivElement>("#modelGrid");
  if (!grid || !appState.selectedFolderId) return;
  await loadDictionaries();
  syncSelectedFolderActions();
  document.querySelector("#folderCrumb")!.textContent = appState.selectedFolderName || "模型库";
  const params = new URLSearchParams({
    folderId: String(appState.selectedFolderId),
    page: String(appState.page),
    pageSize: String(appState.pageSize)
  });
  if (appState.filters.brandId) params.set("brandId", appState.filters.brandId);
  if (appState.filters.typeId) params.set("typeId", appState.filters.typeId);
  if (appState.filters.keyword) params.set("keyword", appState.filters.keyword);
  const result = await api<ModelListResponse>(`/api/models?${params.toString()}`);
  if (result.items.length === 0 && appState.page > 1) {
    appState.page -= 1;
    return loadModels();
  }
  renderPagination({
    container: document.querySelector<HTMLElement>("#modelPagination")!,
    total: result.total,
    page: appState.page,
    pageSize: appState.pageSize,
    onChange: async (page, pageSize) => {
      appState.page = page;
      appState.pageSize = pageSize;
      await loadModels();
    }
  });
  grid.innerHTML = result.items.map(renderModelCard).join("") || `<div class="empty-state">当前目录暂无模型</div>`;
  grid.querySelectorAll<HTMLButtonElement>("[data-action='preview']").forEach((button) => {
    const model = result.items.find((item) => item.id === Number(button.dataset.id));
    if (model) {
      button.addEventListener("click", async () => {
        const { openModelPreview } = await import("./preview");
        openModelPreview(model, loadModels);
      });
    }
  });
  grid.querySelectorAll<HTMLButtonElement>("[data-action='edit']").forEach((button) => {
    button.addEventListener("click", () => editModel(Number(button.dataset.id)));
  });
  grid.querySelectorAll<HTMLButtonElement>("[data-action='import']").forEach((button) => {
    const model = result.items.find((item) => item.id === Number(button.dataset.id));
    if (!model) return;
    button.addEventListener("click", async () => {
      const { createImportScenePayload, emitImportScenePayload } = await import("./preview");
      const payload = createImportScenePayload({
        model,
        basePointEnabled: false,
        basePoint: { x: 0, y: 0, z: 0, rx: 0, ry: 0, rz: 0 },
        selectedOperationIndex: null
      });
      emitImportScenePayload(payload);
      notify("导入场景数据已输出");
    });
  });
  grid.querySelectorAll<HTMLButtonElement>("[data-action='delete']").forEach((button) => {
    button.addEventListener("click", () => deleteModel(Number(button.dataset.id)));
  });
}

async function loadDictionaries(force = false) {
  if (!force && dictionariesLoaded) {
    syncDictionaryFilters();
    return;
  }
  if (!force && dictionariesLoading) {
    await dictionariesLoading;
    syncDictionaryFilters();
    return;
  }

  dictionariesLoading = api<DictionaryResponse>("/api/dictionaries")
    .then((result) => {
      appState.brands = result.brands;
      appState.types = result.types;
      dictionariesLoaded = true;
    })
    .finally(() => {
      dictionariesLoading = null;
    });
  await dictionariesLoading;
  syncDictionaryFilters();
}

function invalidateDictionaries() {
  dictionariesLoaded = false;
}

function syncDictionaryFilters() {
  syncDictionarySelect("#brandFilter", appState.brands, "全部品牌", appState.filters.brandId);
  syncDictionarySelect("#typeFilter", appState.types, "全部类型", appState.filters.typeId);
}

function syncDictionarySelect(selector: string, items: { id: number; name: string }[], emptyText: string, value: string) {
  const select = document.querySelector<HTMLSelectElement>(selector);
  if (!select) return;
  const current = select.value || value;
  select.innerHTML = `<option value="">${emptyText}</option>` + items
    .map((item) => `<option value="${item.id}">${escapeHtml(item.name)}</option>`)
    .join("");
  select.value = current;
}

function renderModelCard(item: ModelItem) {
  const canWrite = item.permissions.write;
  const prop = item.properties ?? {};
  const thumb = item.thumbnail_url
    ? `<img src="${item.thumbnail_url}" alt="" />`
    : `<div class="thumb-placeholder">GLB</div>`;
  return `
    <article class="model-card" data-model-id="${item.id}">
      <div class="model-info">
        <strong>${escapeHtml(item.name)}</strong>
      </div>
      <div class="thumb">
        ${thumb}
        <div class="thumb-actions">
          <button data-action="preview" data-id="${item.id}">预览</button>
          ${canWrite ? `<button data-action="edit" data-id="${item.id}">编辑</button>` : ""}
          <button data-action="import" data-id="${item.id}">导入</button>
          ${canWrite ? `<button data-action="delete" data-id="${item.id}">删除</button>` : ""}
        </div>
      </div>
      <dl>
        <div><dt>品牌</dt><dd>${escapeHtml(item.brand_name ?? "")}</dd></div>
        <div><dt>类型</dt><dd>${escapeHtml(item.type_name ?? "")}</dd></div>
        <div><dt>型号</dt><dd>${escapeHtml(prop.model ?? "")}</dd></div>
        <div><dt>价钱</dt><dd>${escapeHtml(prop.price ?? "")}</dd></div>
        <div><dt>重量</dt><dd>${escapeHtml(prop.weight ?? "")}</dd></div>
      </dl>
    </article>
  `;
}

function syncSelectedFolderActions() {
  const currentFolder = appState.folders.find((folder) => folder.id === appState.selectedFolderId);
  const canWrite = Boolean(currentFolder?.permissions.write);
  const addModelBtn = document.querySelector<HTMLButtonElement>("#addModelBtn");
  const addProcessModelBtn = document.querySelector<HTMLButtonElement>("#addProcessModelBtn");
  if (addModelBtn) addModelBtn.hidden = !canWrite;
  if (addProcessModelBtn) addProcessModelBtn.hidden = !canWrite;
}

export async function uploadModelToBackend(payload: UploadModelPayload) {
  const fileName = (payload.fileName || payload.file.name || payload.name).trim();
  const displayName = (payload.name || modelNameFromFile(fileName)).trim();
  if (!displayName) {
    throw new Error("模型名称不能为空");
  }
  if (!fileName.toLowerCase().endsWith(".glb")) {
    throw new Error("当前阶段只允许上传 .glb 模型");
  }
  const folderId = payload.folderId ?? appState.selectedFolderId;
  if (!folderId) {
    throw new Error("请先选择目录");
  }

  const form = new FormData();
  form.set("folderId", String(folderId));
  form.set("name", modelNameFromFile(displayName));
  form.set("brandName", payload.brandName ?? "");
  form.set("typeName", payload.typeName ?? "");
  form.set("operationTree", normalizeOperationTreeInput(payload.operationTree));
  form.set("file", payload.file, fileName);
  for (const [key, value] of Object.entries(payload.properties ?? {})) {
    form.set(`prop.${key}`, value);
  }

  await api("/api/models/upload", {
    method: "POST",
    body: form
  });
}

async function openUploadModelDialog() {
  if (!appState.selectedFolderId) {
    notify("请先选择目录");
    return;
  }

  const state: UploadFormState = { file: null };
  await loadDictionaries();
  const result = await formDialog<boolean>({
    title: "增加模型",
    width: 560,
    height: 520,
    body: `
      <form id="modelUploadPopupForm" class="popup-form upload-popup-form">
        <label>
          <span>模型文件</span>
          <div id="modelDropZone" class="drop-zone">
            <input id="modelUploadFile" type="file" accept=".glb" />
            <strong>选择模型</strong>
            <em>或拖拽 .glb 模型到这里</em>
            <small id="selectedFileName">未选择文件</small>
          </div>
        </label>
        <label>
          <span>模型名称</span>
          <input id="modelUploadName" name="name" placeholder="选择文件后自动填入" />
        </label>
        <div class="popup-form-grid">
          <label><span>品牌</span><input name="brandName" list="brandOptions" /></label>
          <label><span>类型</span><input name="typeName" list="typeOptions" /></label>
          <label><span>型号</span><input name="model" /></label>
          <label><span>价钱</span><input name="price" /></label>
          <label><span>重量</span><input name="weight" /></label>
        </div>
        ${dictionaryDatalistHtml()}
      </form>
    `,
    onOpen: () => bindUploadDialogEvents(state),
    onSubmit: async () => {
      if (!state.file) {
        throw new Error("请选择 .glb 模型文件");
      }
      const name = document.querySelector<HTMLInputElement>("#modelUploadName")?.value.trim();
      if (!name) {
        throw new Error("模型名称不能为空");
      }
      const form = new FormData(document.querySelector<HTMLFormElement>("#modelUploadPopupForm")!);
      await uploadModelToBackend({
        file: state.file,
        name,
        fileName: state.file.name,
        brandName: String(form.get("brandName") ?? ""),
        typeName: String(form.get("typeName") ?? ""),
        operationTree: defaultOperationTree,
        properties: {
          model: String(form.get("model") ?? ""),
          price: String(form.get("price") ?? ""),
          weight: String(form.get("weight") ?? "")
        }
      });
      return true;
    }
  });

  if (result) {
    invalidateDictionaries();
    await loadModels();
  }
}

export async function openProcessModelUploadDialog(input?: {
  file?: File | null;
  modelName?: string;
  operationTreeJson?: string;
}) {
  if (!appState.selectedFolderId) {
    notify("请先选择目录");
    return;
  }

  const state: UploadFormState = { file: input?.file ?? null };
  await loadDictionaries();
  const initialName = input?.modelName || (input?.file ? modelNameFromFile(input.file.name) : "");
  const result = await formDialog<boolean>({
    title: "上传工艺模型",
    width: 640,
    height: 650,
    body: `
      <form id="processModelUploadPopupForm" class="popup-form upload-popup-form">
        <div class="upload-target-path">
          <span>上传目录</span>
          <strong>${escapeHtml(appState.selectedFolderName || "当前目录")}</strong>
        </div>
        <label>
          <span>模型文件</span>
          <div id="processModelDropZone" class="drop-zone">
            <input id="processModelUploadFile" type="file" accept=".glb" />
            <strong>选择模型</strong>
            <em>或拖拽 .glb 模型到这里</em>
            <small id="processSelectedFileName">${escapeHtml(input?.file?.name ?? "未选择文件")}</small>
          </div>
        </label>
        <label>
          <span>模型名称</span>
          <input id="processModelUploadName" name="name" placeholder="例如 AA.glb" value="${escapeHtml(initialName)}" />
        </label>
        <div class="popup-form-grid">
          <label><span>品牌</span><input name="brandName" list="brandOptions" /></label>
          <label><span>类型</span><input name="typeName" list="typeOptions" /></label>
          <label><span>型号</span><input name="model" /></label>
          <label><span>价钱</span><input name="price" /></label>
          <label><span>重量</span><input name="weight" /></label>
        </div>
        <label>
          <span>工艺数据 JSON</span>
          <textarea id="processOperationTreeJson" name="operationTree" placeholder='可以粘贴 {"OperationTree":"[...]"} 或 OperationTree[] 数组 JSON'>${escapeHtml(input?.operationTreeJson ?? "")}</textarea>
        </label>
        ${dictionaryDatalistHtml()}
      </form>
    `,
    onOpen: () => bindProcessUploadDialogEvents(state),
    onSubmit: async () => {
      if (!state.file) {
        throw new Error("请选择 .glb 模型文件");
      }
      const form = new FormData(document.querySelector<HTMLFormElement>("#processModelUploadPopupForm")!);
      const name = String(form.get("name") ?? "").trim();
      await uploadModelToBackend({
        file: state.file,
        name,
        fileName: name || state.file.name,
        brandName: String(form.get("brandName") ?? ""),
        typeName: String(form.get("typeName") ?? ""),
        operationTree: String(form.get("operationTree") ?? ""),
        properties: {
          model: String(form.get("model") ?? ""),
          price: String(form.get("price") ?? ""),
          weight: String(form.get("weight") ?? "")
        }
      });
      return true;
    }
  });

  if (result) {
    invalidateDictionaries();
    await loadModels();
  }
}

function bindUploadDialogEvents(state: UploadFormState) {
  const dropZone = document.querySelector<HTMLDivElement>("#modelDropZone")!;
  const fileInput = document.querySelector<HTMLInputElement>("#modelUploadFile")!;
  const nameInput = document.querySelector<HTMLInputElement>("#modelUploadName")!;
  const selectedFileName = document.querySelector<HTMLElement>("#selectedFileName")!;

  const selectFile = (file: File) => {
    if (!file.name.toLowerCase().endsWith(".glb")) {
      notify("当前阶段只允许上传 .glb 模型");
      return;
    }
    state.file = file;
    selectedFileName.textContent = file.name;
    if (!nameInput.value.trim()) {
      nameInput.value = modelNameFromFile(file.name);
    }
  };

  fileInput.addEventListener("change", () => {
    const file = fileInput.files?.[0];
    if (file) selectFile(file);
  });

  dropZone.addEventListener("click", (event) => {
    if (event.target !== fileInput) fileInput.click();
  });

  dropZone.addEventListener("dragover", (event) => {
    event.preventDefault();
    dropZone.classList.add("is-dragover");
  });
  dropZone.addEventListener("dragleave", () => {
    dropZone.classList.remove("is-dragover");
  });
  dropZone.addEventListener("drop", (event) => {
    event.preventDefault();
    dropZone.classList.remove("is-dragover");
    const file = event.dataTransfer?.files?.[0];
    if (file) selectFile(file);
  });
}

function bindProcessUploadDialogEvents(state: UploadFormState) {
  const dropZone = document.querySelector<HTMLDivElement>("#processModelDropZone")!;
  const fileInput = document.querySelector<HTMLInputElement>("#processModelUploadFile")!;
  const nameInput = document.querySelector<HTMLInputElement>("#processModelUploadName")!;
  const selectedFileName = document.querySelector<HTMLElement>("#processSelectedFileName")!;

  const selectFile = (file: File) => {
    if (!file.name.toLowerCase().endsWith(".glb")) {
      notify("当前阶段只允许上传 .glb 模型");
      return;
    }
    state.file = file;
    selectedFileName.textContent = file.name;
    if (!nameInput.value.trim()) {
      nameInput.value = file.name;
    }
  };

  if (state.file) {
    selectedFileName.textContent = state.file.name;
    if (!nameInput.value.trim()) {
      nameInput.value = state.file.name;
    }
  }

  fileInput.addEventListener("change", () => {
    const file = fileInput.files?.[0];
    if (file) selectFile(file);
  });

  dropZone.addEventListener("click", (event) => {
    if (event.target !== fileInput) fileInput.click();
  });

  dropZone.addEventListener("dragover", (event) => {
    event.preventDefault();
    dropZone.classList.add("is-dragover");
  });
  dropZone.addEventListener("dragleave", () => {
    dropZone.classList.remove("is-dragover");
  });
  dropZone.addEventListener("drop", (event) => {
    event.preventDefault();
    dropZone.classList.remove("is-dragover");
    const file = event.dataTransfer?.files?.[0];
    if (file) selectFile(file);
  });
}

function normalizeOperationTreeInput(value?: string) {
  const clean = value?.trim();
  if (!clean) return defaultOperationTree;
  let parsed: unknown;
  try {
    parsed = JSON.parse(clean);
  } catch {
    throw new Error("工艺数据必须是合法 JSON 字符串");
  }

  if (Array.isArray(parsed)) {
    return JSON.stringify({ OperationTree: JSON.stringify(parsed) });
  }

  if (parsed && typeof parsed === "object" && "OperationTree" in parsed) {
    const operationTree = (parsed as { OperationTree: unknown }).OperationTree;
    if (typeof operationTree === "string") {
      JSON.parse(operationTree);
      return JSON.stringify({ OperationTree: operationTree });
    }
    if (Array.isArray(operationTree)) {
      return JSON.stringify({ OperationTree: JSON.stringify(operationTree) });
    }
  }

  throw new Error('工艺数据格式必须是 {"OperationTree":"[...]"} 或 OperationTree[] 数组');
}

async function editModel(id: number) {
  const card = document.querySelector<HTMLButtonElement>(`button[data-id="${id}"]`)?.closest(".model-card");
  const oldName = card?.querySelector("strong")?.textContent ?? "";
  await loadDictionaries();
  const result = await formDialog<{ name: string; brandName: string; typeName: string; model: string; price: string; weight: string }>({
    title: "编辑模型",
    width: 520,
    height: 390,
    body: `
      <form id="modelEditPopupForm" class="popup-form">
        <label><span>模型名称</span><input name="name" value="${escapeHtml(oldName)}" /></label>
        <div class="popup-form-grid">
          <label><span>品牌</span><input name="brandName" list="brandOptions" value="${escapeHtml(card?.querySelector("dl div:nth-child(1) dd")?.textContent ?? "")}" /></label>
          <label><span>类型</span><input name="typeName" list="typeOptions" value="${escapeHtml(card?.querySelector("dl div:nth-child(2) dd")?.textContent ?? "")}" /></label>
          <label><span>型号</span><input name="model" value="${escapeHtml(card?.querySelector("dl div:nth-child(3) dd")?.textContent ?? "")}" /></label>
          <label><span>价钱</span><input name="price" value="${escapeHtml(card?.querySelector("dl div:nth-child(4) dd")?.textContent ?? "")}" /></label>
          <label><span>重量</span><input name="weight" value="${escapeHtml(card?.querySelector("dl div:nth-child(5) dd")?.textContent ?? "")}" /></label>
        </div>
        ${dictionaryDatalistHtml()}
      </form>
    `,
    onSubmit: () => {
      const form = new FormData(document.querySelector<HTMLFormElement>("#modelEditPopupForm")!);
      const name = String(form.get("name") ?? "").trim();
      if (!name) {
        throw new Error("模型名称不能为空");
      }
      return {
        name,
        brandName: String(form.get("brandName") ?? ""),
        typeName: String(form.get("typeName") ?? ""),
        model: String(form.get("model") ?? ""),
        price: String(form.get("price") ?? ""),
        weight: String(form.get("weight") ?? "")
      };
    }
  });
  if (!result) return;
  await api(`/api/models/${id}`, {
    method: "PUT",
    body: JSON.stringify({
      name: result.name,
      brandName: result.brandName,
      typeName: result.typeName,
      properties: {
        model: result.model,
        price: result.price,
        weight: result.weight
      }
    })
  });
  invalidateDictionaries();
  await loadModels();
}

function dictionaryDatalistHtml() {
  return `
    <datalist id="brandOptions">
      ${appState.brands.map((item) => `<option value="${escapeHtml(item.name)}"></option>`).join("")}
    </datalist>
    <datalist id="typeOptions">
      ${appState.types.map((item) => `<option value="${escapeHtml(item.name)}"></option>`).join("")}
    </datalist>
  `;
}

async function deleteModel(id: number) {
  const confirmed = await confirmDialog("确认删除该模型？");
  if (!confirmed) return;
  await api(`/api/models/${id}`, { method: "DELETE" });
  await loadModels();
}
