import { initI18n, t } from "./i18n";
import { initSidebar } from "./sidebar";
import {
  defaultFilterSpec,
  defaultRenderSpec,
  type DataSource,
  type FilterSpec,
  type RenderShape,
  type RenderSpec,
  type ScopeMode,
  type TimeHorizon,
  type UserView,
} from "./views/types";

// View editor — /views/new (create) and /views/{slug}/edit (modify).
// The form has a small fixed set of widgets (no full predicate JSON
// editor in v1 — that's a follow-up if power users ask). Saves via
// POST/PATCH /api/user-views.

initI18n();
initSidebar();

interface EditorState {
  mode: "new" | "edit";
  // Set in edit mode after the existing view is fetched.
  existing?: UserView;
}

let state: EditorState = { mode: "new" };

document.addEventListener("DOMContentLoaded", () => {
  state = detectMode();
  bindShapeToggle();
  bindForm();
  bindDelete();
  if (state.mode === "edit") {
    void loadExisting();
    const heading = document.getElementById("editor-heading");
    if (heading) heading.textContent = t("views.editor.heading.edit");
    const del = document.getElementById("editor-delete");
    if (del) del.hidden = false;
  } else {
    seedDefaults();
  }
});

function detectMode(): EditorState {
  const m = window.location.pathname.match(/^\/views\/([^\/]+)\/edit$/);
  if (m) return { mode: "edit" };
  return { mode: "new" };
}

function editSlugFromPath(): string | null {
  const m = window.location.pathname.match(/^\/views\/([^\/]+)\/edit$/);
  return m ? decodeURIComponent(m[1]) : null;
}

async function loadExisting(): Promise<void> {
  const slug = editSlugFromPath();
  if (!slug) return;
  const r = await fetch("/api/user-views", { credentials: "include" });
  if (!r.ok) {
    showFeedback("error", t("views.editor.error.load_failed"));
    return;
  }
  const list = (await r.json()) as UserView[];
  const view = list.find((v) => v.slug === slug);
  if (!view) {
    showFeedback("error", t("views.error.not_found"));
    return;
  }
  state.existing = view;
  populateForm(view);
}

function populateForm(view: UserView): void {
  setInputValue("editor-name", view.name);
  setInputValue("editor-slug", view.slug);
  setSelectValue("editor-icon", view.icon ?? "");
  setCheckboxValue("editor-show-count", view.show_count);

  for (const src of ["deadline", "appointment", "project_event", "approval_request"] as DataSource[]) {
    setCheckboxValue(`source-${src}`, view.filter_spec.sources.includes(src), { name: "source", value: src });
  }

  setSelectValue("editor-scope-mode", view.filter_spec.scope.projects.mode);
  setCheckboxValue("editor-personal-only", view.filter_spec.scope.personal_only ?? false);

  setSelectValue("editor-time-horizon", view.filter_spec.time.horizon);

  setSelectValue("editor-shape", view.render_spec.shape);
  setSelectValue("editor-list-density", view.render_spec.list?.density ?? "comfortable");

  // Hide list-density when shape isn't list.
  toggleListDensityVisibility(view.render_spec.shape);
}

function seedDefaults(): void {
  // Seed the form with a useful blank-slate spec.
  const filter = defaultFilterSpec();
  const render = defaultRenderSpec();
  for (const src of filter.sources) {
    setCheckboxValue(`source-${src}`, true, { name: "source", value: src });
  }
  setSelectValue("editor-scope-mode", filter.scope.projects.mode);
  setSelectValue("editor-time-horizon", filter.time.horizon);
  setSelectValue("editor-shape", render.shape);
  setSelectValue("editor-list-density", render.list?.density ?? "comfortable");
  toggleListDensityVisibility(render.shape);
}

function bindShapeToggle(): void {
  const shapeSelect = document.getElementById("editor-shape") as HTMLSelectElement | null;
  if (!shapeSelect) return;
  shapeSelect.addEventListener("change", () => {
    toggleListDensityVisibility(shapeSelect.value as RenderShape);
  });
}

function toggleListDensityVisibility(shape: RenderShape): void {
  const group = document.getElementById("editor-list-density-group");
  if (!group) return;
  group.style.display = shape === "list" ? "" : "none";
}

function bindForm(): void {
  const form = document.getElementById("editor-form") as HTMLFormElement | null;
  if (!form) return;
  form.addEventListener("submit", async (e) => {
    e.preventDefault();
    const payload = collectForm();
    if (!payload) return; // collectForm already shows feedback
    if (state.mode === "edit" && state.existing) {
      await save("PATCH", `/api/user-views/${state.existing.id}`, payload);
    } else {
      await save("POST", `/api/user-views`, payload);
    }
  });
}

function bindDelete(): void {
  const btn = document.getElementById("editor-delete") as HTMLButtonElement | null;
  if (!btn) return;
  btn.addEventListener("click", async () => {
    if (!state.existing) return;
    if (!confirm(t("views.editor.confirm_delete"))) return;
    const r = await fetch(`/api/user-views/${state.existing.id}`, {
      method: "DELETE",
      credentials: "include",
    });
    if (!r.ok) {
      showFeedback("error", t("views.editor.error.delete_failed"));
      return;
    }
    window.location.href = "/views";
  });
}

interface CreatePayload {
  slug: string;
  name: string;
  icon?: string;
  filter_spec: FilterSpec;
  render_spec: RenderSpec;
  show_count: boolean;
}

function collectForm(): CreatePayload | null {
  const name = getInputValue("editor-name").trim();
  const slug = getInputValue("editor-slug").trim();
  const iconRaw = getSelectValue("editor-icon");
  const icon = iconRaw === "" ? undefined : iconRaw;
  const showCount = getCheckboxValue("editor-show-count");

  if (!name) {
    showFeedback("error", t("views.editor.error.name_required"));
    return null;
  }
  if (!/^[a-z0-9][a-z0-9-]{0,62}$/.test(slug)) {
    showFeedback("error", t("views.editor.error.slug_format"));
    return null;
  }

  const sources: DataSource[] = (["deadline", "appointment", "project_event", "approval_request"] as DataSource[])
    .filter((s) => getCheckboxValue(`source-${s}`));
  if (sources.length === 0) {
    showFeedback("error", t("views.editor.error.sources_required"));
    return null;
  }

  const scopeMode = getSelectValue("editor-scope-mode") as ScopeMode;
  const personalOnly = getCheckboxValue("editor-personal-only");
  const horizon = getSelectValue("editor-time-horizon") as TimeHorizon;

  const shape = getSelectValue("editor-shape") as RenderShape;
  const listDensity = getSelectValue("editor-list-density") as "comfortable" | "compact";

  const filter: FilterSpec = {
    version: 1,
    sources,
    scope: {
      projects: { mode: scopeMode },
      personal_only: personalOnly,
    },
    time: { horizon, field: "auto" },
  };
  const render: RenderSpec = {
    shape,
    list: shape === "list" ? { density: listDensity, sort: "date_asc" } : undefined,
    cards: shape === "cards" ? { group_by: "day", sort: "date_asc" } : undefined,
    calendar: shape === "calendar" ? { default_view: "month" } : undefined,
  };

  return { slug, name, icon, filter_spec: filter, render_spec: render, show_count: showCount };
}

async function save(method: "POST" | "PATCH", url: string, payload: CreatePayload): Promise<void> {
  const r = await fetch(url, {
    method,
    credentials: "include",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  if (!r.ok) {
    const body = await r.json().catch(() => ({} as { error?: string }));
    showFeedback("error", body.error || `${r.status}: ${r.statusText}`);
    return;
  }
  // Saved — go to the saved view.
  window.location.href = `/views/${encodeURIComponent(payload.slug)}`;
}

// ----- DOM helpers -----

function getInputValue(id: string): string {
  const el = document.getElementById(id) as HTMLInputElement | null;
  return el?.value ?? "";
}

function setInputValue(id: string, value: string): void {
  const el = document.getElementById(id) as HTMLInputElement | null;
  if (el) el.value = value;
}

function getSelectValue(id: string): string {
  const el = document.getElementById(id) as HTMLSelectElement | null;
  return el?.value ?? "";
}

function setSelectValue(id: string, value: string): void {
  const el = document.getElementById(id) as HTMLSelectElement | null;
  if (el) el.value = value;
}

function getCheckboxValue(id: string): boolean {
  const el = document.getElementById(id) as HTMLInputElement | null;
  if (el) return el.checked;
  // Fallback: lookup by name+value (for the source-* checkbox group).
  const m = id.match(/^source-(.+)$/);
  if (m) {
    const cb = document.querySelector<HTMLInputElement>(`input[name="source"][value="${m[1]}"]`);
    return !!cb?.checked;
  }
  return false;
}

function setCheckboxValue(id: string, value: boolean, fallback?: { name: string; value: string }): void {
  let el = document.getElementById(id) as HTMLInputElement | null;
  if (!el && fallback) {
    el = document.querySelector<HTMLInputElement>(`input[name="${fallback.name}"][value="${fallback.value}"]`);
  }
  if (el) el.checked = value;
}

function showFeedback(kind: "success" | "error", text: string): void {
  const el = document.getElementById("editor-feedback");
  if (!el) return;
  el.textContent = text;
  el.classList.remove("form-msg-success", "form-msg-error");
  el.classList.add(kind === "success" ? "form-msg-success" : "form-msg-error");
  el.hidden = false;
}
