import { initI18n, onLangChange, t } from "./i18n";
import { initSidebar } from "./sidebar";
import { initProjectTree, refreshProjectTree, rerenderProjectTree } from "./project-tree";
import { renderFlatList, ProjectFlatRow } from "./projects-flat";
import { renderCardsView, teardownCardsView } from "./projects-cards";

// /projects orchestrator (t-paliad-149).
//
// Owns:
//   - chip state (scope + status + type + pinned + has_open_deadlines)
//   - search term (in-place filter, server-side)
//   - view mode (tree | flat). Cards lands in PR 2.
//   - last-view restore + URL params (Q1 lock-in: last-viewed restore).
//
// Delegates rendering to:
//   - project-tree.ts for tree mode
//   - projects-flat.ts for flat-table mode

type ViewMode = "tree" | "cards" | "flat";
type Scope = "all" | "mine" | "pinned";

interface Chips {
  scope: Scope;
  status: Set<string>;
  type: Set<string>;
  hasOpenDeadlines: boolean;
}

interface State {
  viewMode: ViewMode;
  chips: Chips;
  searchQuery: string;
}

const STORAGE_KEY = "paliad.projects.lastView";
const SEARCH_DEBOUNCE_MS = 250;

let state: State = defaultState();
let flatRows: ProjectFlatRow[] | null = null;
let searchDebounce: number | null = null;

function defaultState(): State {
  return {
    viewMode: "tree",
    chips: {
      scope: "all",
      status: new Set(),
      type: new Set(),
      hasOpenDeadlines: false,
    },
    searchQuery: "",
  };
}

function loadStoredState(): State | null {
  try {
    const raw = sessionStorage.getItem(STORAGE_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw) as {
      viewMode?: ViewMode;
      chips?: { scope?: Scope; status?: string[]; type?: string[]; hasOpenDeadlines?: boolean };
      searchQuery?: string;
    };
    const viewMode: ViewMode =
      parsed.viewMode === "flat" ? "flat" :
      parsed.viewMode === "cards" ? "cards" :
      "tree";
    return {
      viewMode,
      chips: {
        scope: parsed.chips?.scope === "mine" || parsed.chips?.scope === "pinned" ? parsed.chips.scope : "all",
        status: new Set(parsed.chips?.status || []),
        type: new Set(parsed.chips?.type || []),
        hasOpenDeadlines: !!parsed.chips?.hasOpenDeadlines,
      },
      searchQuery: typeof parsed.searchQuery === "string" ? parsed.searchQuery : "",
    };
  } catch {
    return null;
  }
}

function saveState() {
  try {
    sessionStorage.setItem(STORAGE_KEY, JSON.stringify({
      viewMode: state.viewMode,
      chips: {
        scope: state.chips.scope,
        status: [...state.chips.status],
        type: [...state.chips.type],
        hasOpenDeadlines: state.chips.hasOpenDeadlines,
      },
      searchQuery: state.searchQuery,
    }));
  } catch {
    /* private mode, quota — ignore */
  }
}

// applyURL overlays ?view=, ?scope=, ?status=, ?type=, ?has_open_deadlines=,
// ?q= onto the current state. URL > sessionStorage > defaults.
function applyURL() {
  const url = new URL(window.location.href);
  const v = url.searchParams.get("view");
  if (v === "tree" || v === "flat" || v === "cards") state.viewMode = v;
  const sc = url.searchParams.get("scope");
  if (sc === "mine" || sc === "pinned" || sc === "all") state.chips.scope = sc;
  const status = url.searchParams.get("status");
  if (status !== null) {
    state.chips.status = new Set(status.split(",").map((s) => s.trim()).filter(Boolean));
  }
  const type = url.searchParams.get("type");
  if (type !== null) {
    state.chips.type = new Set(type.split(",").map((s) => s.trim()).filter(Boolean));
  }
  const has = url.searchParams.get("has_open_deadlines");
  if (has === "true" || has === "false") state.chips.hasOpenDeadlines = has === "true";
  const q = url.searchParams.get("q");
  if (q !== null) state.searchQuery = q;
}

function syncURL() {
  const url = new URL(window.location.href);
  // Clear all known params, then re-set only the non-default ones (keeps URLs short).
  ["view", "scope", "status", "type", "has_open_deadlines", "q"].forEach((k) => url.searchParams.delete(k));
  if (state.viewMode !== "tree") url.searchParams.set("view", state.viewMode);
  if (state.chips.scope !== "all") url.searchParams.set("scope", state.chips.scope);
  if (state.chips.status.size > 0) url.searchParams.set("status", [...state.chips.status].join(","));
  if (state.chips.type.size > 0) url.searchParams.set("type", [...state.chips.type].join(","));
  if (state.chips.hasOpenDeadlines) url.searchParams.set("has_open_deadlines", "true");
  if (state.searchQuery.trim()) url.searchParams.set("q", state.searchQuery.trim());
  window.history.replaceState({}, "", url.toString());
}

// Build the query string the tree endpoint expects. Same shape as the URL
// state but always written (we don't omit "all" because the server expects
// ?subtree_counts=true to get the new field).
function treeParams(): URLSearchParams {
  const p = new URLSearchParams();
  if (state.chips.scope !== "all") p.set("scope", state.chips.scope);
  if (state.chips.status.size > 0) p.set("status", [...state.chips.status].join(","));
  if (state.chips.type.size > 0) p.set("type", [...state.chips.type].join(","));
  if (state.chips.hasOpenDeadlines) p.set("has_open_deadlines", "true");
  if (state.searchQuery.trim()) p.set("q", state.searchQuery.trim());
  p.set("subtree_counts", "true");
  return p;
}

function reflectChipsToDOM() {
  // Scope toggles
  const scopes: Scope[] = ["all", "mine", "pinned"];
  scopes.forEach((s) => {
    const btn = document.querySelector<HTMLButtonElement>(`.projects-chip[data-chip="${s}"]`);
    btn?.classList.toggle("is-active", state.chips.scope === s);
  });
  // Has-open-deadlines
  const hasBtn = document.querySelector<HTMLButtonElement>(`.projects-chip[data-chip="has_open_deadlines"]`);
  hasBtn?.classList.toggle("is-active", state.chips.hasOpenDeadlines);

  // Multi-select panels
  reflectMulti("status", state.chips.status);
  reflectMulti("type", state.chips.type);

  // View mode segment-control
  document.querySelectorAll<HTMLButtonElement>(".projects-view-btn").forEach((btn) => {
    btn.classList.toggle("is-active", btn.dataset.view === state.viewMode);
  });

  // Search input value (when restoring state on init)
  const searchInput = document.getElementById("projects-search") as HTMLInputElement | null;
  if (searchInput && searchInput.value !== state.searchQuery) {
    searchInput.value = state.searchQuery;
  }
}

function reflectMulti(name: string, set: Set<string>) {
  const wrap = document.querySelector<HTMLDetailsElement>(`.projects-chip-multi[data-chip-multi="${name}"]`);
  if (!wrap) return;
  const summary = wrap.querySelector<HTMLElement>("summary");
  const inputs = wrap.querySelectorAll<HTMLInputElement>('input[type="checkbox"]');
  inputs.forEach((cb) => { cb.checked = set.has(cb.value); });
  if (summary) {
    summary.classList.toggle("is-active", set.size > 0);
    const baseLabel = t(`projects.chip.${name}` as never) || (name === "status" ? "Status" : "Typ");
    if (set.size === 0) {
      summary.textContent = String(baseLabel);
    } else if (set.size === 1) {
      const sole = [...set][0];
      const labelKey = `projects.chip.${name}.${sole}` as never;
      const label = t(labelKey) || sole;
      summary.textContent = `${baseLabel}: ${label}`;
    } else {
      const tmpl = t("projects.chip.multi.count" as never) || "{n} ausgewählt";
      summary.textContent = `${baseLabel}: ${String(tmpl).replace("{n}", String(set.size))}`;
    }
  }
}

function setScope(s: Scope) {
  state.chips.scope = s;
  postChipChange();
}

function toggleHasOpen() {
  state.chips.hasOpenDeadlines = !state.chips.hasOpenDeadlines;
  postChipChange();
}

function postChipChange() {
  syncURL();
  saveState();
  reflectChipsToDOM();
  void render();
}

function clearAllChips() {
  state = { ...state, chips: defaultState().chips, searchQuery: "" };
  postChipChange();
  const searchInput = document.getElementById("projects-search") as HTMLInputElement | null;
  if (searchInput) searchInput.value = "";
}

async function render() {
  const treeWrap = document.getElementById("projekt-tree-wrap")!;
  const tableWrap = document.getElementById("entity-table-wrap")!;
  const cardsWrap = document.getElementById("projects-cards-wrap")!;
  const empty = document.getElementById("entity-empty")!;
  const emptyFiltered = document.getElementById("entity-empty-filtered")!;

  if (state.viewMode === "tree") {
    teardownCardsView();
    treeWrap.style.display = "block";
    tableWrap.style.display = "none";
    cardsWrap.style.display = "none";
    empty.style.display = "none";
    emptyFiltered.style.display = "none";
    const container = document.getElementById("projekt-tree-container") as HTMLElement;
    await initProjectTree(container, treeParams());
    return;
  }

  if (state.viewMode === "cards") {
    treeWrap.style.display = "none";
    tableWrap.style.display = "none";
    empty.style.display = "none";
    emptyFiltered.style.display = "none";
    await renderCardsView({ treeParams: treeParams() });
    return;
  }

  // Flat-list mode. Reuses /api/projects (existing flat endpoint).
  teardownCardsView();
  treeWrap.style.display = "none";
  if (!flatRows) {
    flatRows = await loadFlatRows();
  }
  if (!flatRows) {
    tableWrap.style.display = "none";
    return;
  }
  const filtered = filterFlatRows(flatRows);
  const count = document.getElementById("projects-count")!;
  count.textContent = `${filtered.length} / ${flatRows.length}`;
  if (flatRows.length === 0) {
    tableWrap.style.display = "none";
    empty.style.display = "block";
    emptyFiltered.style.display = "none";
    return;
  }
  if (filtered.length === 0) {
    tableWrap.style.display = "none";
    empty.style.display = "none";
    emptyFiltered.style.display = "block";
    return;
  }
  tableWrap.style.display = "";
  empty.style.display = "none";
  emptyFiltered.style.display = "none";
  renderFlatList({ rows: filtered });
}

async function loadFlatRows(): Promise<ProjectFlatRow[] | null> {
  const unavailable = document.getElementById("entity-unavailable")!;
  try {
    const resp = await fetch("/api/projects");
    if (resp.status === 503) {
      unavailable.style.display = "block";
      return null;
    }
    if (!resp.ok) {
      unavailable.style.display = "block";
      return null;
    }
    return (await resp.json()) as ProjectFlatRow[];
  } catch {
    unavailable.style.display = "block";
    return null;
  }
}

function filterFlatRows(rows: ProjectFlatRow[]): ProjectFlatRow[] {
  let out = rows;
  if (state.chips.status.size > 0) {
    out = out.filter((p) => state.chips.status.has(p.status));
  }
  if (state.chips.type.size > 0) {
    out = out.filter((p) => state.chips.type.has(p.type));
  }
  // Note: scope=mine / scope=pinned / has_open_deadlines are not applied
  // to the flat-list view — those need server-side support and the flat
  // endpoint /api/projects is unchanged from pre-redesign. The chips simply
  // narrow status + type in flat mode; tree mode honours all chips.
  if (state.searchQuery.trim()) {
    const q = state.searchQuery.toLowerCase();
    out = out.filter((p) => {
      const haystack = [
        p.title,
        p.reference || "",
        p.client_number || "",
        p.matter_number || "",
      ]
        .join(" ")
        .toLowerCase();
      return haystack.includes(q);
    });
  }
  return out;
}

function initSearch() {
  const input = document.getElementById("projects-search") as HTMLInputElement | null;
  if (!input) return;
  input.addEventListener("input", () => {
    if (searchDebounce !== null) {
      window.clearTimeout(searchDebounce);
    }
    searchDebounce = window.setTimeout(() => {
      state.searchQuery = input.value;
      syncURL();
      saveState();
      void render();
    }, SEARCH_DEBOUNCE_MS);
  });
}

function initChips() {
  document.querySelectorAll<HTMLButtonElement>(".projects-chip[data-chip]").forEach((btn) => {
    const chip = btn.dataset.chip!;
    if (chip === "all") {
      btn.addEventListener("click", () => clearAllChips());
    } else if (chip === "mine") {
      btn.addEventListener("click", () => setScope(state.chips.scope === "mine" ? "all" : "mine"));
    } else if (chip === "pinned") {
      btn.addEventListener("click", () => setScope(state.chips.scope === "pinned" ? "all" : "pinned"));
    } else if (chip === "has_open_deadlines") {
      btn.addEventListener("click", () => toggleHasOpen());
    }
  });

  // Multi-select panels — wire each checkbox change.
  document.querySelectorAll<HTMLDetailsElement>(".projects-chip-multi").forEach((wrap) => {
    const name = wrap.dataset.chipMulti!;
    const set = (name === "status" ? state.chips.status : state.chips.type);
    wrap.querySelectorAll<HTMLInputElement>('input[type="checkbox"]').forEach((cb) => {
      cb.addEventListener("change", () => {
        if (cb.checked) set.add(cb.value); else set.delete(cb.value);
        postChipChange();
      });
    });
  });

  const reset = document.getElementById("projects-reset-filters");
  if (reset) reset.addEventListener("click", () => clearAllChips());
}

function initViewSegment() {
  document.querySelectorAll<HTMLButtonElement>(".projects-view-btn").forEach((btn) => {
    btn.addEventListener("click", () => {
      const v = btn.dataset.view as ViewMode;
      if (v !== "tree" && v !== "flat" && v !== "cards") return;
      if (state.viewMode === v) return;
      state.viewMode = v;
      syncURL();
      saveState();
      reflectChipsToDOM();
      void render();
    });
  });
}

document.addEventListener("DOMContentLoaded", () => {
  initI18n();
  initSidebar();
  // Q1 lock-in: last-viewed restore. URL > sessionStorage > defaults.
  const stored = loadStoredState();
  if (stored) state = stored;
  applyURL();
  reflectChipsToDOM();
  initSearch();
  initChips();
  initViewSegment();
  onLangChange(() => {
    reflectChipsToDOM();
    if (state.viewMode === "tree") {
      rerenderProjectTree();
    } else {
      void render();
    }
  });
  void render();
  // The pin handler in project-tree.ts mutates the per-node cache and then
  // invalidates it, so subsequent chip changes refetch with fresh pin data.
  // When the user navigates back to /projects via popstate (in-app links),
  // re-apply URL state.
  window.addEventListener("popstate", () => {
    state = loadStoredState() || defaultState();
    applyURL();
    reflectChipsToDOM();
    refreshProjectTree(treeParams());
    flatRows = null;
    void render();
  });
});
