import React, { useEffect, useMemo, useState } from "react";
import axios from "axios";
import { Button, Modal, ModalBody, ModalContent, ModalHeader, useDisclosure } from "@heroui/react";
import Markdown from "react-markdown";
import { useLanguageStore } from "@/stores/languageStore";
import { useTranslation } from "react-i18next";
import AdminRulesetForm from "@/components/admin/rulesets/AdminRulesetForm";
import AdminRulesetsTable from "@/components/admin/rulesets/AdminRulesetsTable";
import {
  emptyForm,
  isBlank,
  isIntegerLike,
  isNumberLike,
  numberValue,
  toIntOrNull,
  toNumberOrNull,
  type EvaluationRuleSet,
  type RuleSetForm,
  type RuleSetFormMode,
} from "@/components/admin/rulesets/adminRulesetTypes";
import rulesetDoc from "../../docs/EvaluationRuleSet.md?raw";

const RULESET_LOADERS = {
  en: () => import("../locales/en/ruleset.json"),
  cs: () => import("../locales/cs/ruleset.json"),
} as const;

type PaginatedResponse<T> = { data: T[] };

export default function AdminEvaluationPage() {
  const locale = useLanguageStore((s) => s.locale);
  const { t } = useTranslation("common");
  const { t: tRules, i18n } = useTranslation("ruleset");
  const valuePlaceholder = t("value_na") ?? "—";
  const label = (key: string, fallback: string) => (t(key) as string) ?? fallback;
  const [items, setItems] = useState<EvaluationRuleSet[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [refreshKey, setRefreshKey] = useState(0);
  const [formMode, setFormMode] = useState<RuleSetFormMode>("none");
  const [editing, setEditing] = useState<EvaluationRuleSet | null>(null);
  const [form, setForm] = useState<RuleSetForm>(emptyForm);
  const [initialForm, setInitialForm] = useState<RuleSetForm>(emptyForm);
  const [submitting, setSubmitting] = useState(false);
  const [formError, setFormError] = useState<string | null>(null);
  const [formSuccess, setFormSuccess] = useState<string | null>(null);
  const { isOpen: helpOpen, onOpen: openHelp, onOpenChange: onHelpChange } = useDisclosure();

  useEffect(() => {
    let active = true;
    const loadNamespace = async (lang: "cs" | "en") => {
      if (i18n.hasResourceBundle(lang, "ruleset")) return;
      const loader = RULESET_LOADERS[lang];
      if (!loader) return;
      const module = await loader();
      if (!active) return;
      const data = module.default ?? module;
      i18n.addResourceBundle(lang, "ruleset", data, true, true);
    };

    const lang = i18n.language?.startsWith("cs") ? "cs" : "en";
    loadNamespace(lang);
    if (lang !== "en") {
      loadNamespace("en");
    }

    return () => {
      active = false;
    };
  }, [i18n, i18n.language]);

  useEffect(() => {
    let active = true;
    (async () => {
      try {
        setLoading(true);
        setError(null);
        const res = await axios.get<PaginatedResponse<EvaluationRuleSet> | EvaluationRuleSet[]>(
          "/api/evaluation-rule-sets",
          {
            headers: { Accept: "application/json" },
            params: { per_page: 200 },
            withCredentials: true,
          }
        );
        if (!active) return;
        const data = Array.isArray(res.data) ? res.data : res.data.data;
        setItems(data);
      } catch {
        if (!active) return;
        setError(t("admin_rulesets_load_failed") ?? "Nepodařilo se načíst rule sety.");
      } finally {
        if (active) setLoading(false);
      }
    })();
    return () => {
      active = false;
    };
  }, [locale, refreshKey]);

  const resetForm = () => {
    setForm(emptyForm);
    setInitialForm(emptyForm);
    setEditing(null);
    setFormError(null);
    setFormSuccess(null);
  };

  const helpLabel = (labelText: string, key: string) => {
    const fieldKey = key.startsWith("ruleset_help_") ? key.slice("ruleset_help_".length) : key;
    return (
      <span className="inline-flex flex-col leading-tight" title={tRules(key)}>
        <span>{labelText}</span>
        <span className="text-[0.8em] text-foreground-400 font-mono">{fieldKey}</span>
      </span>
    );
  };

  const formatDate = (value: string | null | undefined, lang: string) => {
    if (!value) return valuePlaceholder;
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return valuePlaceholder;
    return new Intl.DateTimeFormat(lang, {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
    }).format(date);
  };

  const openEdit = (item: EvaluationRuleSet) => {
    resetForm();
    setEditing(item);
    setFormMode("edit");
    const nextForm: RuleSetForm = {
      name: item.name ?? "",
      code: item.code ?? "",
      description: item.description ?? "",
      scoring_mode: item.scoring_mode ?? "DISTANCE",
      points_per_qso: numberValue(item.points_per_qso),
      points_per_km: numberValue(item.points_per_km),
      use_multipliers: !!item.use_multipliers,
      multiplier_type: item.multiplier_type ?? "WWL",
      dup_qso_policy: item.dup_qso_policy === "COUNT_ONCE" ? "ZERO_POINTS" : item.dup_qso_policy ?? "ZERO_POINTS",
      nil_qso_policy: item.nil_qso_policy ?? "PENALTY",
      no_counterpart_log_policy: item.no_counterpart_log_policy ?? item.nil_qso_policy ?? "PENALTY",
      not_in_counterpart_log_policy:
        item.not_in_counterpart_log_policy ?? item.nil_qso_policy ?? "PENALTY",
      unique_qso_policy: item.unique_qso_policy ?? "ZERO_POINTS",
      busted_call_policy: item.busted_call_policy ?? "PENALTY",
      busted_rst_policy: item.busted_rst_policy ?? "ZERO_POINTS",
      busted_exchange_policy: item.busted_exchange_policy ?? "ZERO_POINTS",
      busted_serial_policy: item.busted_serial_policy ?? item.busted_exchange_policy ?? "ZERO_POINTS",
      busted_locator_policy: item.busted_locator_policy ?? item.busted_exchange_policy ?? "ZERO_POINTS",
      penalty_dup_points: numberValue(item.penalty_dup_points),
      penalty_nil_points: numberValue(item.penalty_nil_points),
      penalty_busted_call_points: numberValue(item.penalty_busted_call_points),
      penalty_busted_rst_points: numberValue(item.penalty_busted_rst_points),
      penalty_busted_exchange_points: numberValue(item.penalty_busted_exchange_points),
      penalty_busted_serial_points: numberValue(item.penalty_busted_serial_points),
      penalty_busted_locator_points: numberValue(item.penalty_busted_locator_points),
      penalty_out_of_window_points: numberValue(item.penalty_out_of_window_points),
      dupe_scope: item.dupe_scope ?? "BAND",
      callsign_normalization: item.callsign_normalization ?? "IGNORE_SUFFIX",
      distance_rounding: item.distance_rounding ?? "FLOOR",
      min_distance_km: numberValue(item.min_distance_km),
      require_locators: item.require_locators ?? true,
      out_of_window_policy: item.out_of_window_policy ?? "INVALID",
      exchange_type: item.exchange_type ?? "SERIAL_WWL",
      exchange_requires_wwl: item.exchange_requires_wwl ?? true,
      exchange_requires_serial: item.exchange_requires_serial ?? true,
      exchange_requires_report: item.exchange_requires_report ?? false,
      exchange_pattern: item.exchange_pattern ?? "",
      match_tiebreak_order: Array.isArray(item.match_tiebreak_order)
        ? item.match_tiebreak_order.join(", ")
        : "",
      match_require_locator_match: item.match_require_locator_match ?? false,
      match_require_exchange_match: item.match_require_exchange_match ?? false,
      multiplier_scope: item.multiplier_scope ?? "PER_BAND",
      multiplier_source: item.multiplier_source ?? "VALID_ONLY",
      wwl_multiplier_level: item.wwl_multiplier_level ?? "LOCATOR_6",
      checklog_matching: item.checklog_matching ?? true,
      out_of_window_dq_threshold: numberValue(item.out_of_window_dq_threshold),
      time_diff_dq_threshold_percent: numberValue(item.time_diff_dq_threshold_percent),
      time_diff_dq_threshold_sec: numberValue(item.time_diff_dq_threshold_sec),
      bad_qso_dq_threshold_percent: numberValue(item.bad_qso_dq_threshold_percent),
      time_tolerance_sec: numberValue(item.time_tolerance_sec),
      allow_time_shift_one_hour: item.allow_time_shift_one_hour ?? true,
      time_shift_seconds: numberValue(item.time_shift_seconds),
      time_mismatch_policy: item.time_mismatch_policy ?? "FLAG_ONLY",
      allow_time_mismatch_pairing: item.allow_time_mismatch_pairing ?? true,
      time_mismatch_max_sec: numberValue(item.time_mismatch_max_sec),
      require_unique_qso: item.require_unique_qso ?? true,
      ignore_slash_part: item.ignore_slash_part ?? true,
      ignore_third_part: item.ignore_third_part ?? true,
      rst_ignore_third_char: item.rst_ignore_third_char ?? true,
      callsign_suffix_max_len: numberValue(item.callsign_suffix_max_len),
      callsign_levenshtein_max: numberValue(item.callsign_levenshtein_max),
      letters_in_rst: item.letters_in_rst ?? true,
      discard_qso_rec_diff_call: item.discard_qso_rec_diff_call ?? true,
      discard_qso_sent_diff_call: item.discard_qso_sent_diff_call ?? false,
      discard_qso_rec_diff_rst: item.discard_qso_rec_diff_rst ?? true,
      discard_qso_sent_diff_rst: item.discard_qso_sent_diff_rst ?? false,
      discard_qso_rec_diff_serial: item.discard_qso_rec_diff_serial ?? true,
      discard_qso_sent_diff_serial: item.discard_qso_sent_diff_serial ?? false,
      discard_qso_rec_diff_wwl: item.discard_qso_rec_diff_wwl ?? true,
      discard_qso_sent_diff_wwl: item.discard_qso_sent_diff_wwl ?? false,
      discard_qso_rec_diff_code: item.discard_qso_rec_diff_code ?? true,
      discard_qso_sent_diff_code: item.discard_qso_sent_diff_code ?? false,
      dup_resolution_strategy: Array.isArray(item.dup_resolution_strategy)
        ? item.dup_resolution_strategy.join(", ")
        : "",
      operating_window_mode: item.operating_window_mode ?? "NONE",
      operating_window_hours: numberValue(item.operating_window_hours),
      sixhr_ranking_mode: item.sixhr_ranking_mode ?? "IARU",
    };
    setForm(nextForm);
    setInitialForm(nextForm);
  };

  const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault();
    setFormError(null);
    setFormSuccess(null);
    if (hasValidationErrors) {
      setFormError(t("admin_rulesets_fix_errors") ?? "Opravte chyby ve formuláři.");
      return;
    }


    const payload: Record<string, unknown> = {
      name: form.name.trim(),
      code: form.code.trim(),
      description: form.description.trim() || null,
      scoring_mode: form.scoring_mode,
      points_per_qso: toNumberOrNull(form.points_per_qso),
      points_per_km: toNumberOrNull(form.points_per_km),
      use_multipliers: form.use_multipliers,
      multiplier_type: form.multiplier_type,
      dup_qso_policy: form.dup_qso_policy,
      nil_qso_policy: form.nil_qso_policy,
      no_counterpart_log_policy: form.no_counterpart_log_policy,
      not_in_counterpart_log_policy: form.not_in_counterpart_log_policy,
      unique_qso_policy: form.unique_qso_policy,
      busted_call_policy: form.busted_call_policy,
      busted_rst_policy: form.busted_rst_policy,
      busted_exchange_policy: form.busted_exchange_policy,
      busted_serial_policy: form.busted_serial_policy,
      busted_locator_policy: form.busted_locator_policy,
      penalty_dup_points: toIntOrNull(form.penalty_dup_points),
      penalty_nil_points: toIntOrNull(form.penalty_nil_points),
      penalty_busted_call_points: toIntOrNull(form.penalty_busted_call_points),
      penalty_busted_rst_points: toIntOrNull(form.penalty_busted_rst_points),
      penalty_busted_exchange_points: toIntOrNull(form.penalty_busted_exchange_points),
      penalty_busted_serial_points: toIntOrNull(form.penalty_busted_serial_points),
      penalty_busted_locator_points: toIntOrNull(form.penalty_busted_locator_points),
      penalty_out_of_window_points: toIntOrNull(form.penalty_out_of_window_points),
      dupe_scope: form.dupe_scope,
      callsign_normalization: form.callsign_normalization,
      distance_rounding: form.distance_rounding,
      min_distance_km: toNumberOrNull(form.min_distance_km),
      require_locators: form.require_locators,
      out_of_window_policy: form.out_of_window_policy,
      exchange_type: form.exchange_type,
      exchange_requires_wwl: form.exchange_requires_wwl,
      exchange_requires_serial: form.exchange_requires_serial,
      exchange_requires_report: form.exchange_requires_report,
      exchange_pattern: form.exchange_pattern.trim() || null,
      match_tiebreak_order: form.match_tiebreak_order.trim()
        ? form.match_tiebreak_order.split(",").map((v) => v.trim()).filter(Boolean)
        : null,
      match_require_locator_match: form.match_require_locator_match,
      match_require_exchange_match: form.match_require_exchange_match,
      multiplier_scope: form.multiplier_scope,
      multiplier_source: form.multiplier_source,
      wwl_multiplier_level: form.wwl_multiplier_level,
      checklog_matching: form.checklog_matching,
      out_of_window_dq_threshold: toNumberOrNull(form.out_of_window_dq_threshold),
      time_diff_dq_threshold_percent: toIntOrNull(form.time_diff_dq_threshold_percent),
      time_diff_dq_threshold_sec: toIntOrNull(form.time_diff_dq_threshold_sec),
      bad_qso_dq_threshold_percent: toIntOrNull(form.bad_qso_dq_threshold_percent),
      time_tolerance_sec: toNumberOrNull(form.time_tolerance_sec),
      allow_time_shift_one_hour: form.allow_time_shift_one_hour,
      time_shift_seconds: toIntOrNull(form.time_shift_seconds),
      time_mismatch_policy: form.time_mismatch_policy,
      allow_time_mismatch_pairing: form.allow_time_mismatch_pairing,
      time_mismatch_max_sec: toIntOrNull(form.time_mismatch_max_sec),
      require_unique_qso: form.require_unique_qso,
      ignore_slash_part: form.ignore_slash_part,
      ignore_third_part: form.ignore_third_part,
      rst_ignore_third_char: form.rst_ignore_third_char,
      callsign_suffix_max_len: toIntOrNull(form.callsign_suffix_max_len),
      callsign_levenshtein_max: toIntOrNull(form.callsign_levenshtein_max),
      letters_in_rst: form.letters_in_rst,
      discard_qso_rec_diff_call: form.discard_qso_rec_diff_call,
      discard_qso_sent_diff_call: form.discard_qso_sent_diff_call,
      discard_qso_rec_diff_rst: form.discard_qso_rec_diff_rst,
      discard_qso_sent_diff_rst: form.discard_qso_sent_diff_rst,
      discard_qso_rec_diff_serial: form.discard_qso_rec_diff_serial,
      discard_qso_sent_diff_serial: form.discard_qso_sent_diff_serial,
      discard_qso_rec_diff_wwl: form.discard_qso_rec_diff_wwl,
      discard_qso_sent_diff_wwl: form.discard_qso_sent_diff_wwl,
      discard_qso_rec_diff_code: form.discard_qso_rec_diff_code,
      discard_qso_sent_diff_code: form.discard_qso_sent_diff_code,
      dup_resolution_strategy: form.dup_resolution_strategy.trim()
        ? form.dup_resolution_strategy.split(",").map((v) => v.trim()).filter(Boolean)
        : null,
      operating_window_mode: form.operating_window_mode,
      operating_window_hours: toIntOrNull(form.operating_window_hours),
      sixhr_ranking_mode: form.sixhr_ranking_mode,
      options: null,
    };

    try {
      setSubmitting(true);
      await axios.get("/sanctum/csrf-cookie", { withCredentials: true });
      if (formMode === "edit" && editing) {
        await axios.put(`/api/evaluation-rule-sets/${editing.id}`, payload, {
          headers: { Accept: "application/json" },
          withCredentials: true,
          withXSRFToken: true,
        });
        setFormSuccess(t("admin_rulesets_updated") ?? "Rule set byl upraven.");
      } else {
        await axios.post(`/api/evaluation-rule-sets`, payload, {
          headers: { Accept: "application/json" },
          withCredentials: true,
          withXSRFToken: true,
        });
        setFormSuccess(t("admin_rulesets_created") ?? "Rule set byl vytvořen.");
      }
      setFormMode("none");
      resetForm();
      setRefreshKey((k) => k + 1);
    } catch (e: any) {
      setFormError(
        e?.response?.data?.message ?? (t("admin_rulesets_save_failed") ?? "Chyba při ukládání rule setu.")
      );
    } finally {
      setSubmitting(false);
    }
  };

  const visibleItems = useMemo(() => items, [items]);
  const validation = useMemo(() => {
    const numberError = (value: string) =>
      value.trim() && !isNumberLike(value)
        ? t("validation_number") ?? "Musí být číslo."
        : null;
    const intError = (value: string) =>
      value.trim() && !isIntegerLike(value)
        ? t("validation_integer") ?? "Musí být celé číslo."
        : null;
    return {
      name: isBlank(form.name)
        ? t("validation_name_required") ?? "Název je povinný."
        : null,
      code: isBlank(form.code)
        ? t("validation_code_required") ?? "Kód je povinný."
        : null,
      points_per_qso: numberError(form.points_per_qso),
      points_per_km: numberError(form.points_per_km),
      min_distance_km: numberError(form.min_distance_km),
      time_tolerance_sec: intError(form.time_tolerance_sec),
      out_of_window_dq_threshold: intError(form.out_of_window_dq_threshold),
      time_diff_dq_threshold_percent: intError(form.time_diff_dq_threshold_percent) ||
        (form.time_diff_dq_threshold_percent.trim() &&
        (Number(form.time_diff_dq_threshold_percent) < 1 || Number(form.time_diff_dq_threshold_percent) > 100)
          ? t("validation_range_1_100") ?? "Rozsah 1–100."
          : null),
      time_diff_dq_threshold_sec: intError(form.time_diff_dq_threshold_sec) ||
        (form.time_diff_dq_threshold_sec.trim() && Number(form.time_diff_dq_threshold_sec) < 1
          ? t("validation_min_one") ?? "Musí být alespoň 1."
          : null),
      bad_qso_dq_threshold_percent: intError(form.bad_qso_dq_threshold_percent) ||
        (form.bad_qso_dq_threshold_percent.trim() &&
        (Number(form.bad_qso_dq_threshold_percent) < 1 || Number(form.bad_qso_dq_threshold_percent) > 100)
          ? t("validation_range_1_100") ?? "Rozsah 1–100."
          : null),
      callsign_suffix_max_len: intError(form.callsign_suffix_max_len) ||
        (form.callsign_suffix_max_len.trim() && Number(form.callsign_suffix_max_len) < 0
          ? t("validation_min_zero") ?? "Musí být alespoň 0."
          : null),
      callsign_levenshtein_max: intError(form.callsign_levenshtein_max) ||
        (form.callsign_levenshtein_max.trim() &&
        (Number(form.callsign_levenshtein_max) < 0 || Number(form.callsign_levenshtein_max) > 2)
          ? t("validation_range_0_2") ?? "Rozsah 0–2."
          : null),
      time_shift_seconds: intError(form.time_shift_seconds) ||
        (form.time_shift_seconds.trim() && Number(form.time_shift_seconds) < 0
          ? t("validation_min_zero") ?? "Musí být alespoň 0."
          : null),
      time_mismatch_max_sec: intError(form.time_mismatch_max_sec) ||
        (form.time_mismatch_max_sec.trim() && Number(form.time_mismatch_max_sec) < 0
          ? t("validation_min_zero") ?? "Musí být alespoň 0."
          : null),
      penalty_dup_points: intError(form.penalty_dup_points),
      penalty_nil_points: intError(form.penalty_nil_points),
      penalty_busted_call_points: intError(form.penalty_busted_call_points),
      penalty_busted_rst_points: intError(form.penalty_busted_rst_points),
      penalty_busted_exchange_points: intError(form.penalty_busted_exchange_points),
      penalty_busted_serial_points: intError(form.penalty_busted_serial_points),
      penalty_busted_locator_points: intError(form.penalty_busted_locator_points),
      penalty_out_of_window_points: intError(form.penalty_out_of_window_points),
    };
  }, [form]);

  const hasValidationErrors = useMemo(
    () => Object.values(validation).some(Boolean),
    [validation]
  );

  const isFormDirty = useMemo(
    () => JSON.stringify(form) !== JSON.stringify(initialForm),
    [form, initialForm]
  );

  const reportRequirementWarning =
    !form.exchange_requires_report &&
    (form.discard_qso_rec_diff_rst || form.discard_qso_sent_diff_rst);

  const closeForm = () => {
    if (isFormDirty) {
      const confirmed = window.confirm(
        t("admin_form_confirm_close") ??
          "Formulář obsahuje neuložené změny. Opravdu ho chcete zavřít?"
      );
      if (!confirmed) return;
    }
    setFormMode("none");
    resetForm();
  };

  return (
    <div className="space-y-6">
      <div className="flex gap-3 items-center">
        <h1 className="text-xl font-semibold">{t("admin_rulesets_title") ?? "Evaluation rules"}</h1>
        {formMode !== "none" && (
          <Button variant="light" onPress={closeForm}>
            {t("admin_form_close") ?? "Zavřít formulář"}
          </Button>
        )}
      </div>

      {loading ? (
        <div>{t("admin_rulesets_loading") ?? "Načítám rule sety…"}</div>
      ) : error ? (
        <div className="text-sm text-red-600">{error}</div>
      ) : (
        <AdminRulesetsTable
          items={visibleItems}
          locale={locale}
          valuePlaceholder={valuePlaceholder}
          formatDate={formatDate}
          onEdit={openEdit}
          t={(key) => t(key) as string}
        />
      )}

      {!loading && !error && (
        <div className="flex flex-wrap gap-3">
          <Button
            type="button"
            color="primary"
            onPress={() => {
              resetForm();
              setFormMode("create");
            }}
          >
            {label("admin_rulesets_create", "Nová sada pravidel")}
          </Button>
          <Button type="button" variant="bordered" onPress={openHelp}>
            {label("admin_rulesets_help", "Nápověda")}
          </Button>
          {formMode !== "none" && (
            <Button type="button" variant="bordered" onPress={closeForm}>
              {label("admin_form_close", "Zavřít formulář")}
            </Button>
          )}
        </div>
      )}

      {formMode !== "none" && (
        <AdminRulesetForm
          formMode={formMode === "create" ? "create" : "edit"}
          form={form}
          setForm={setForm}
          validation={validation}
          formError={formError}
          formSuccess={formSuccess}
          reportRequirementWarning={reportRequirementWarning}
          submitting={submitting}
          hasValidationErrors={hasValidationErrors}
          label={label}
          helpLabel={helpLabel}
          tRules={tRules}
          onSubmit={handleSubmit}
          onClose={closeForm}
        />
      )}
      <Modal isOpen={helpOpen} onOpenChange={onHelpChange} size="5xl" scrollBehavior="inside">
        <ModalContent>
          <ModalHeader className="flex flex-col gap-1">
            {label("admin_rulesets_help_title", "Dokumentace rulesetu")}
          </ModalHeader>
          <ModalBody>
            <div className="prose max-w-none text-sm">
              <Markdown>{rulesetDoc}</Markdown>
            </div>
          </ModalBody>
        </ModalContent>
      </Modal>
    </div>
  );
}
