"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { get, put } from "@/lib/api";
import { Badge, Notice, Spinner, Toggle } from "@/components/ui";
import { DataGrid, type ColDef } from "@/components/DataGrid";
import { Modal } from "@/components/Modal";
import { MoreIcon } from "@/components/icons";
import { useSaveItem } from "@/components/SaveBus";
import { SERVICE_INFO } from "@/lib/glossary";

type Schedule = { mode: "always" | "window"; days: number[]; start: string; end: string };
type Service = {
  name: string;
  label: string;
  enabled: boolean;
  withinSchedule: boolean;
  activeNow: boolean;
  reason: string;
  schedule: Schedule;
};
type Row = Service & { description: string; _dirty: boolean };

const DAYS: [number, string][] = [
  [1, "Mon"], [2, "Tue"], [3, "Wed"], [4, "Thu"], [5, "Fri"], [6, "Sat"], [7, "Sun"],
];

function scheduleSummary(s: Schedule): string {
  if (s.mode === "always") return "Always on";
  const days = s.days.length === 7 ? "Every day" : DAYS.filter(([d]) => s.days.includes(d)).map(([, l]) => l).join(", ") || "no days";
  return `${days} · ${s.start}–${s.end}`;
}

/** Effective-state badge for the grid (mirrors the kill-switch + schedule logic). */
function StateCell(p: { data?: Row }) {
  const svc = p.data;
  if (!svc) return null;
  const badge = svc.activeNow ? (
    <Badge kind="ok">active now</Badge>
  ) : svc.reason === "outside schedule" ? (
    <Badge kind="warn">outside schedule</Badge>
  ) : svc.reason === "kill-switch active" ? (
    <Badge kind="danger">kill-switch</Badge>
  ) : (
    <Badge kind="neutral">stopped</Badge>
  );
  return (
    <span className="flex items-center gap-2">
      {badge}
      {svc._dirty && <Badge kind="warn">unsaved</Badge>}
    </span>
  );
}

function MoreCell(p: { data?: Row; context?: { onMore: (name: string) => void } }) {
  if (!p.data) return null;
  const name = p.data.name;
  return (
    <button
      type="button"
      onClick={() => p.context?.onMore(name)}
      aria-label={`Settings for ${p.data.label}`}
      title="Settings"
      className="rounded-sm p-1 text-slate-500 hover:bg-slate-100 hover:text-slate-800"
    >
      <MoreIcon className="h-5 w-5" />
    </button>
  );
}

export default function ServicesPanel() {
  const [services, setServices] = useState<Service[] | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [dirty, setDirty] = useState<Record<string, true>>({});
  const [modalName, setModalName] = useState<string | null>(null);

  const load = useCallback(async () => {
    try {
      const d = await get<{ services: Service[] }>("services");
      setServices(d.services);
      setDirty({});
      setError(null);
    } catch (e) {
      setError((e as Error).message);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  // Stable save handler for the floating Save button: PUT each changed service.
  const servicesRef = useRef<Service[] | null>(null);
  const dirtyRef = useRef<Record<string, true>>({});
  servicesRef.current = services;
  dirtyRef.current = dirty;

  const saveAll = useCallback(async () => {
    for (const svc of servicesRef.current ?? []) {
      if (dirtyRef.current[svc.name]) {
        await put(`services/${svc.name}`, { enabled: svc.enabled, schedule: svc.schedule });
      }
    }
    await load();
  }, [load]);

  useSaveItem("services", Object.keys(dirty).length > 0, saveAll);

  const markDirty = (name: string) => setDirty((p) => ({ ...p, [name]: true }));
  const patch = useCallback((name: string, change: Partial<Service>) => {
    setServices((s) => s!.map((x) => (x.name === name ? { ...x, ...change } : x)));
    markDirty(name);
  }, []);
  const patchSchedule = useCallback((name: string, change: Partial<Schedule>) => {
    setServices((s) => s!.map((x) => (x.name === name ? { ...x, schedule: { ...x.schedule, ...change } } : x)));
    markDirty(name);
  }, []);

  const rowData = useMemo<Row[]>(
    () => (services ?? []).map((s) => ({ ...s, description: SERVICE_INFO[s.name] ?? "", _dirty: !!dirty[s.name] })),
    [services, dirty],
  );

  const columnDefs = useMemo<ColDef[]>(
    () => [
      { field: "label", headerName: "Service", flex: 1, minWidth: 180 },
      { field: "description", headerName: "Description", flex: 2, minWidth: 280, tooltipField: "description", wrapText: true, autoHeight: true, cellClass: "leading-snug text-slate-600" },
      { headerName: "Current state", colId: "state", minWidth: 190, sortable: false, filter: false, cellRenderer: StateCell, cellClass: "flex items-center" },
      { headerName: "", colId: "actions", width: 70, pinned: "right", sortable: false, filter: false, resizable: false, cellRenderer: MoreCell, cellClass: "flex items-center justify-center" },
    ],
    [],
  );

  const gridContext = useMemo(() => ({ onMore: (name: string) => setModalName(name) }), []);

  if (error) return <Notice kind="error">Could not load services: {error}</Notice>;
  if (!services) return <Spinner label="Loading services…" />;

  const modalSvc = services.find((s) => s.name === modalName) ?? null;

  return (
    <>
      <p className="mb-4 text-sm text-slate-600">
        Start/stop each pipeline service and schedule when it may run. A service works only when it is on, inside its
        time window, and the kill-switch is off — otherwise it idles. Open a service&apos;s <strong>⋯ More</strong> menu
        to change its settings; changes are saved together with the floating <em>Save</em> button.
      </p>

      <DataGrid rowData={rowData} columnDefs={columnDefs} context={gridContext} getRowId={(p: { data: Row }) => p.data.name} autoHeight />

      <Modal open={!!modalSvc} onClose={() => setModalName(null)} title={modalSvc ? modalSvc.label : ""}>
        {modalSvc && <ServiceSettings svc={modalSvc} patch={patch} patchSchedule={patchSchedule} />}
      </Modal>
    </>
  );
}

function ServiceSettings({
  svc,
  patch,
  patchSchedule,
}: {
  svc: Service;
  patch: (name: string, change: Partial<Service>) => void;
  patchSchedule: (name: string, change: Partial<Schedule>) => void;
}) {
  return (
    <div className="space-y-5">
      {SERVICE_INFO[svc.name] && <p className="text-sm text-slate-600">{SERVICE_INFO[svc.name]}</p>}

      <div className="rounded-lg border border-slate-200 p-4">
        <h3 className="mb-2 text-sm font-semibold text-slate-800">Status</h3>
        <Toggle
          id={`enabled-${svc.name}`}
          checked={svc.enabled}
          onChange={(v) => patch(svc.name, { enabled: v })}
          label={svc.enabled ? "Active (running)" : "Passive (stopped)"}
        />
      </div>

      <fieldset className="rounded-lg border border-slate-200 p-4">
        <legend className="px-1 text-sm font-semibold text-slate-800">Availability</legend>
        <div className="flex flex-wrap items-center gap-4">
          {(["always", "window"] as const).map((m) => (
            <label key={m} className="flex items-center gap-2 text-sm">
              <input
                type="radio"
                name={`mode-${svc.name}`}
                checked={svc.schedule.mode === m}
                onChange={() => patchSchedule(svc.name, { mode: m })}
              />
              {m === "always" ? "Always on" : "On a timetable"}
            </label>
          ))}
        </div>

        {svc.schedule.mode === "window" && (
          <div className="mt-4 space-y-4">
            <div>
              <span className="label">Days</span>
              <div className="mt-1 flex flex-wrap gap-1">
                {DAYS.map(([d, lbl]) => {
                  const on = svc.schedule.days.includes(d);
                  return (
                    <button
                      key={d}
                      type="button"
                      aria-pressed={on}
                      onClick={() => patchSchedule(svc.name, { days: on ? svc.schedule.days.filter((x) => x !== d) : [...svc.schedule.days, d].sort() })}
                      className={`rounded-sm px-2.5 py-1 text-xs font-medium ${on ? "bg-brand text-white" : "border border-slate-300 bg-white text-slate-600"}`}
                    >
                      {lbl}
                    </button>
                  );
                })}
              </div>
            </div>
            <div className="flex flex-wrap items-end gap-4">
              <div>
                <label htmlFor={`start-${svc.name}`} className="label">From</label>
                <input id={`start-${svc.name}`} type="time" value={svc.schedule.start} onChange={(e) => patchSchedule(svc.name, { start: e.target.value })} className="input" />
              </div>
              <div>
                <label htmlFor={`end-${svc.name}`} className="label">To</label>
                <input id={`end-${svc.name}`} type="time" value={svc.schedule.end} onChange={(e) => patchSchedule(svc.name, { end: e.target.value })} className="input" />
              </div>
              <p className="text-xs text-slate-500">Times are Europe/Zurich.</p>
            </div>
          </div>
        )}
        <p className="mt-3 text-xs text-slate-500">Current: {scheduleSummary(svc.schedule)}</p>
      </fieldset>

      <Notice kind="info">Changes apply when you click the floating <em>Save</em> button.</Notice>
    </div>
  );
}
