/**
 * Jail detail page.
 *
 * Displays full configuration and state for a single fail2ban jail:
 * - Status badges and control buttons (start, stop, idle, reload)
 * - Log paths, fail-regex, ignore-regex patterns
 * - Date pattern, encoding, and actions
 * - Ignore list management (add / remove IPs)
 */

import { useState } from "react";
import {
  Badge,
  Button,
  Field,
  Input,
  MessageBar,
  MessageBarBody,
  Spinner,
  Switch,
  Text,
  Tooltip,
  makeStyles,
  tokens,
} from "@fluentui/react-components";
import { useCommonSectionStyles } from "../theme/commonStyles";
import {
  ArrowClockwiseRegular,
  ArrowLeftRegular,
  ArrowSyncRegular,
  DismissRegular,
  PauseRegular,
  PlayRegular,
  StopRegular,
} from "@fluentui/react-icons";
import { Link, useNavigate, useParams } from "react-router-dom";
import { useJailDetail, useJailBannedIps } from "../hooks/useJails";
import { formatSeconds } from "../utils/formatDate";
import type { Jail } from "../types/jail";
import { BannedIpsSection } from "../components/jail/BannedIpsSection";

// ---------------------------------------------------------------------------
// Styles
// ---------------------------------------------------------------------------

const useStyles = makeStyles({
  root: {
    display: "flex",
    flexDirection: "column",
    gap: tokens.spacingVerticalL,
  },
  breadcrumb: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalS,
  },

  headerRow: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalM,
    flexWrap: "wrap",
  },
  controlRow: {
    display: "flex",
    flexWrap: "wrap",
    gap: tokens.spacingHorizontalS,
  },
  grid: {
    display: "grid",
    gridTemplateColumns: "max-content 1fr",
    gap: `${tokens.spacingVerticalXS} ${tokens.spacingHorizontalM}`,
    alignItems: "baseline",
  },
  label: {
    fontWeight: tokens.fontWeightSemibold,
    color: tokens.colorNeutralForeground2,
  },
  mono: {
    fontFamily: "Consolas, 'Courier New', monospace",
    fontSize: tokens.fontSizeBase200,
  },
  codeList: {
    display: "flex",
    flexDirection: "column",
    gap: tokens.spacingVerticalXXS,
    paddingTop: tokens.spacingVerticalXS,
  },
  codeItem: {
    fontFamily: "Consolas, 'Courier New', monospace",
    fontSize: tokens.fontSizeBase200,
    padding: `2px ${tokens.spacingHorizontalS}`,
    backgroundColor: tokens.colorNeutralBackground2,
    borderRadius: tokens.borderRadiusSmall,
    wordBreak: "break-all",
  },
  centred: {
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    padding: tokens.spacingVerticalXXL,
  },
  ignoreRow: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    gap: tokens.spacingHorizontalS,
    padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalS}`,
    backgroundColor: tokens.colorNeutralBackground2,
    borderRadius: tokens.borderRadiusSmall,
  },
  formRow: {
    display: "flex",
    gap: tokens.spacingHorizontalM,
    alignItems: "flex-end",
    flexWrap: "wrap",
  },
  formField: { minWidth: "200px", flexGrow: 1 },
});

// ---------------------------------------------------------------------------
// Components
// ---------------------------------------------------------------------------

function CodeList({ items, empty }: { items: string[]; empty: string }): React.JSX.Element {
  const styles = useStyles();
  if (items.length === 0) {
    return <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>{empty}</Text>;
  }
  return (
    <div className={styles.codeList}>
      {items.map((item, i) => (
        <span key={i} className={styles.codeItem}>
          {item}
        </span>
      ))}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Sub-component: Jail info card
// ---------------------------------------------------------------------------

interface JailInfoProps {
  jail: Jail;
  onRefresh: () => void;
  onStart: () => Promise<void>;
  onStop: () => Promise<void>;
  onSetIdle: (on: boolean) => Promise<void>;
  onReload: () => Promise<void>;
}

function JailInfoSection({ jail, onRefresh, onStart, onStop, onSetIdle, onReload }: JailInfoProps): React.JSX.Element {
  const styles = useStyles();
  const sectionStyles = useCommonSectionStyles();
  const navigate = useNavigate();
  const [ctrlError, setCtrlError] = useState<string | null>(null);

  const handle =
    (fn: () => Promise<unknown>, postNavigate = false) =>
    (): void => {
      setCtrlError(null);
      fn()
        .then(() => {
          if (postNavigate) {
            navigate("/jails");
          } else {
            onRefresh();
          }
        })
        .catch((err: unknown) => {
          const msg =
            err instanceof Error
              ? err.message
              : String(err);
          setCtrlError(msg);
        });
    };

  return (
    <div className={sectionStyles.section}>
      <div className={sectionStyles.sectionHeader}>
        <div className={styles.headerRow}>
          <Text
            size={600}
            weight="semibold"
            style={{ fontFamily: "Consolas, 'Courier New', monospace" }}
          >
            {jail.name}
          </Text>
          {jail.running ? (
            jail.idle ? (
              <Badge appearance="filled" color="warning">idle</Badge>
            ) : (
              <Badge appearance="filled" color="success">running</Badge>
            )
          ) : (
            <Badge appearance="filled" color="danger">stopped</Badge>
          )}
        </div>
        <Button
          size="small"
          appearance="subtle"
          icon={<ArrowClockwiseRegular />}
          onClick={onRefresh}
          aria-label="Refresh"
        />
      </div>

      {ctrlError && (
        <MessageBar intent="error">
          <MessageBarBody>{ctrlError}</MessageBarBody>
        </MessageBar>
      )}

      {/* Control buttons */}
      <div className={styles.controlRow}>
        {jail.running ? (
          <Tooltip content="Stop jail" relationship="label">
            <Button
              appearance="secondary"
              icon={<StopRegular />}
              onClick={handle(onStop)}
            >
              Stop
            </Button>
          </Tooltip>
        ) : (
          <Tooltip content="Start jail" relationship="label">
            <Button
              appearance="primary"
              icon={<PlayRegular />}
              onClick={handle(onStart)}
            >
              Start
            </Button>
          </Tooltip>
        )}
        <Tooltip
          content={jail.idle ? "Resume from idle mode" : "Pause monitoring (idle mode)"}
          relationship="label"
        >
          <Button
            appearance="outline"
            icon={<PauseRegular />}
            onClick={handle(() => onSetIdle(!jail.idle))}
            disabled={!jail.running}
          >
            {jail.idle ? "Resume" : "Set Idle"}
          </Button>
        </Tooltip>
        <Tooltip content="Reload jail configuration" relationship="label">
          <Button
            appearance="outline"
            icon={<ArrowSyncRegular />}
            onClick={handle(onReload)}
          >
            Reload
          </Button>
        </Tooltip>
      </div>

      {/* Stats grid */}
      {jail.status && (
        <div className={styles.grid} style={{ marginTop: tokens.spacingVerticalS }}>
          <Text className={styles.label}>Currently banned:</Text>
          <Text>{String(jail.status.currently_banned)}</Text>
          <Text className={styles.label}>Total banned:</Text>
          <Text>{String(jail.status.total_banned)}</Text>
          <Text className={styles.label}>Currently failed:</Text>
          <Text>{String(jail.status.currently_failed)}</Text>
          <Text className={styles.label}>Total failed:</Text>
          <Text>{String(jail.status.total_failed)}</Text>
        </div>
      )}

      {/* Config grid */}
      <div className={styles.grid} style={{ marginTop: tokens.spacingVerticalS }}>
        <Text className={styles.label}>Backend:</Text>
        <Text className={styles.mono}>{jail.backend}</Text>
        <Text className={styles.label}>Find time:</Text>
        <Text>{formatSeconds(jail.find_time)}</Text>
        <Text className={styles.label}>Ban time:</Text>
        <Text>{formatSeconds(jail.ban_time)}</Text>
        <Text className={styles.label}>Max retry:</Text>
        <Text>{String(jail.max_retry)}</Text>
        {jail.date_pattern && (
          <>
            <Text className={styles.label}>Date pattern:</Text>
            <Text className={styles.mono}>{jail.date_pattern}</Text>
          </>
        )}
        {jail.log_encoding && (
          <>
            <Text className={styles.label}>Log encoding:</Text>
            <Text className={styles.mono}>{jail.log_encoding}</Text>
          </>
        )}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Sub-component: Patterns section
// ---------------------------------------------------------------------------

function PatternsSection({ jail }: { jail: Jail }): React.JSX.Element {
  const sectionStyles = useCommonSectionStyles();
  return (
    <div className={sectionStyles.section}>
      <div className={sectionStyles.sectionHeader}>
        <Text as="h2" size={500} weight="semibold">
          Log Paths &amp; Patterns
        </Text>
      </div>

      <Text size={300} weight="semibold">Log Paths</Text>
      <CodeList items={jail.log_paths} empty="No log paths configured." />

      <Text size={300} weight="semibold" style={{ marginTop: tokens.spacingVerticalS }}>
        Fail Regex
      </Text>
      <CodeList items={jail.fail_regex} empty="No fail-regex patterns." />

      <Text size={300} weight="semibold" style={{ marginTop: tokens.spacingVerticalS }}>
        Ignore Regex
      </Text>
      <CodeList items={jail.ignore_regex} empty="No ignore-regex patterns." />

      {jail.actions.length > 0 && (
        <>
          <Text size={300} weight="semibold" style={{ marginTop: tokens.spacingVerticalS }}>
            Actions
          </Text>
          <CodeList items={jail.actions} empty="" />
        </>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Sub-component: Ban-time escalation section
// ---------------------------------------------------------------------------

function BantimeEscalationSection({ jail }: { jail: Jail }): React.JSX.Element | null {
  const styles = useStyles();
  const sectionStyles = useCommonSectionStyles();
  const esc = jail.bantime_escalation;
  if (!esc?.increment) return null;

  return (
    <div className={sectionStyles.section}>
      <div className={sectionStyles.sectionHeader}>
        <Text as="h2" size={500} weight="semibold">
          Ban-time Escalation
        </Text>
        <Badge appearance="filled" color="informative">enabled</Badge>
      </div>
      <div className={styles.grid}>
        {esc.factor !== null && (
          <>
            <Text className={styles.label}>Factor:</Text>
            <Text className={styles.mono}>{String(esc.factor)}</Text>
          </>
        )}
        {esc.formula && (
          <>
            <Text className={styles.label}>Formula:</Text>
            <Text className={styles.mono}>{esc.formula}</Text>
          </>
        )}
        {esc.multipliers && (
          <>
            <Text className={styles.label}>Multipliers:</Text>
            <Text className={styles.mono}>{esc.multipliers}</Text>
          </>
        )}
        {esc.max_time !== null && (
          <>
            <Text className={styles.label}>Max time:</Text>
            <Text>{formatSeconds(esc.max_time)}</Text>
          </>
        )}
        {esc.rnd_time !== null && (
          <>
            <Text className={styles.label}>Random jitter:</Text>
            <Text>{formatSeconds(esc.rnd_time)}</Text>
          </>
        )}
        <Text className={styles.label}>Count across all jails:</Text>
        <Text>{esc.overall_jails ? "yes" : "no"}</Text>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Sub-component: Ignore list section
// ---------------------------------------------------------------------------

interface IgnoreListSectionProps {
  jailName: string;
  ignoreList: string[];
  ignoreSelf: boolean;
  onAdd: (ip: string) => Promise<void>;
  onRemove: (ip: string) => Promise<void>;
  onToggleIgnoreSelf: (on: boolean) => Promise<void>;
}

function IgnoreListSection({
  jailName: _jailName,
  ignoreList,
  ignoreSelf,
  onAdd,
  onRemove,
  onToggleIgnoreSelf,
}: IgnoreListSectionProps): React.JSX.Element {
  const styles = useStyles();
  const sectionStyles = useCommonSectionStyles();
  const [inputVal, setInputVal] = useState("");
  const [opError, setOpError] = useState<string | null>(null);

  const handleAdd = (): void => {
    if (!inputVal.trim()) return;
    setOpError(null);
    onAdd(inputVal.trim())
      .then(() => {
        setInputVal("");
      })
      .catch((err: unknown) => {
        const msg = err instanceof Error ? err.message : String(err);
        setOpError(msg);
      });
  };

  const handleRemove = (ip: string): void => {
    setOpError(null);
    onRemove(ip).catch((err: unknown) => {
      const msg = err instanceof Error ? err.message : String(err);
      setOpError(msg);
    });
  };

  return (
    <div className={sectionStyles.section}>
      <div className={sectionStyles.sectionHeader}>
        <div style={{ display: "flex", alignItems: "center", gap: tokens.spacingHorizontalM }}>
          <Text as="h2" size={500} weight="semibold">
            Ignore List (IP Whitelist)
          </Text>
        </div>
        <Badge appearance="tint">{String(ignoreList.length)}</Badge>
      </div>

      {/* Ignore-self toggle */}
      <Switch
        label="Ignore self — exclude this server's own IP addresses from banning"
        checked={ignoreSelf}
        onChange={(_e, data): void => {
          onToggleIgnoreSelf(data.checked).catch((err: unknown) => {
            const msg = err instanceof Error ? err.message : String(err);
            setOpError(msg);
          });
        }}
      />

      {opError && (
        <MessageBar intent="error">
          <MessageBarBody>{opError}</MessageBarBody>
        </MessageBar>
      )}

      {/* Add form */}
      <div className={styles.formRow}>
        <div className={styles.formField}>
          <Field label="Add IP or CIDR network">
            <Input
              placeholder="e.g. 10.0.0.0/8 or 192.168.1.1"
              value={inputVal}
              onChange={(_, d) => {
                setInputVal(d.value);
              }}
              onKeyDown={(e) => {
                if (e.key === "Enter") handleAdd();
              }}
            />
          </Field>
        </div>
        <Button
          appearance="primary"
          onClick={handleAdd}
          disabled={!inputVal.trim()}
          style={{ marginBottom: "4px" }}
        >
          Add
        </Button>
      </div>

      {/* List */}
      {ignoreList.length === 0 ? (
        <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
          The ignore list is empty.
        </Text>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
          {ignoreList.map((ip) => (
            <div key={ip} className={styles.ignoreRow}>
              <Text className={styles.mono}>{ip}</Text>
              <Tooltip content={`Remove ${ip} from ignore list`} relationship="label">
                <Button
                  size="small"
                  appearance="subtle"
                  icon={<DismissRegular />}
                  onClick={() => {
                    handleRemove(ip);
                  }}
                  aria-label={`Remove ${ip}`}
                />
              </Tooltip>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page component
// ---------------------------------------------------------------------------

/**
 * Jail detail page.
 *
 * Fetches and displays the full configuration and state of a single jail
 * identified by the `:name` route parameter.
 */
export function JailDetailPage(): React.JSX.Element {
  const styles = useStyles();
  const { name = "" } = useParams<{ name: string }>();
  const { jail, ignoreList, ignoreSelf, loading, error, refresh, addIp, removeIp, toggleIgnoreSelf, start, stop, reload, setIdle } =
    useJailDetail(name);
  const {
    items,
    total,
    page,
    pageSize,
    search,
    loading: bannedLoading,
    error: bannedError,
    opError,
    refresh: refreshBanned,
    setPage,
    setPageSize,
    setSearch,
    unban,
  } = useJailBannedIps(name);

  if (loading && !jail) {
    return (
      <div className={styles.centred}>
        <Spinner label={`Loading jail ${name}…`} />
      </div>
    );
  }

  if (error) {
    return (
      <div className={styles.root}>
        <Link to="/jails" style={{ textDecoration: "none" }}>
          <Button appearance="subtle" icon={<ArrowLeftRegular />}>
            Back to Jails
          </Button>
        </Link>
        <MessageBar intent="error">
          <MessageBarBody>Failed to load jail {name}: {error}</MessageBarBody>
        </MessageBar>
      </div>
    );
  }

  if (!jail) return <></>;

  return (
    <div className={styles.root}>
      {/* Breadcrumb */}
      <div className={styles.breadcrumb}>
        <Link to="/jails" style={{ textDecoration: "none" }}>
          <Button appearance="subtle" size="small" icon={<ArrowLeftRegular />}>
            Jails
          </Button>
        </Link>
        <Text size={200} style={{ color: tokens.colorNeutralForeground3 }}>
          /
        </Text>
        <Text size={200} className={styles.mono}>
          {name}
        </Text>
      </div>

      <JailInfoSection jail={jail} onRefresh={refresh} onStart={start} onStop={stop} onReload={reload} onSetIdle={setIdle} />
      <BannedIpsSection
        items={items}
        total={total}
        page={page}
        pageSize={pageSize}
        search={search}
        loading={bannedLoading}
        error={bannedError}
        opError={opError}
        onSearch={setSearch}
        onPageChange={setPage}
        onPageSizeChange={setPageSize}
        onRefresh={refreshBanned}
        onUnban={unban}
      />
      <PatternsSection jail={jail} />
      <BantimeEscalationSection jail={jail} />
      <IgnoreListSection
        jailName={name}
        ignoreList={ignoreList}
        ignoreSelf={ignoreSelf}
        onAdd={addIp}
        onRemove={removeIp}
        onToggleIgnoreSelf={toggleIgnoreSelf}
      />
    </div>
  );
}
