/**
 * Setup wizard page.
 *
 * Displayed automatically on first launch when no configuration exists.
 * Once submitted successfully the user is redirected to the login page.
 * All fields use Fluent UI v9 components and inline validation.
 */

import { useEffect, useState, type ChangeEvent, type FormEvent } from "react";
import {
  Button,
  Field,
  Input,
  makeStyles,
  MessageBar,
  MessageBarBody,
  Spinner,
  Text,
  tokens,
} from "@fluentui/react-components";
import { useNavigate } from "react-router-dom";
import { useSetup } from "../hooks/useSetup";

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

const useStyles = makeStyles({
  root: {
    display: "flex",
    justifyContent: "center",
    alignItems: "flex-start",
    minHeight: "100vh",
    padding: `${tokens.spacingVerticalXXL} ${tokens.spacingHorizontalM}`,
    backgroundColor: tokens.colorNeutralBackground2,
  },
  card: {
    width: "100%",
    maxWidth: "480px",
    backgroundColor: tokens.colorNeutralBackground1,
    borderRadius: tokens.borderRadiusXLarge,
    padding: tokens.spacingVerticalXXL,
    boxShadow: tokens.shadow8,
  },
  heading: {
    marginBottom: tokens.spacingVerticalL,
    display: "block",
  },
  description: {
    marginBottom: tokens.spacingVerticalXXL,
    color: tokens.colorNeutralForeground2,
    display: "block",
  },
  fields: {
    display: "flex",
    flexDirection: "column",
    gap: tokens.spacingVerticalM,
  },
  submitRow: {
    marginTop: tokens.spacingVerticalL,
  },
  error: {
    marginBottom: tokens.spacingVerticalM,
  },
  passwordStrength: {
    marginTop: tokens.spacingVerticalS,
    display: "grid",
    gap: tokens.spacingVerticalS,
  },
  passwordStrengthBar: {
    display: "grid",
    gridTemplateColumns: "repeat(4, 1fr)",
    gap: tokens.spacingHorizontalS,
    width: "100%",
  },
  passwordStrengthSegment: {
    height: "8px",
    borderRadius: tokens.borderRadiusSmall,
    backgroundColor: tokens.colorNeutralStroke2,
  },
  passwordStrengthSegmentActive: {
    backgroundColor: tokens.colorPaletteGreenBorder1,
  },
  passwordRuleList: {
    margin: 0,
    paddingLeft: tokens.spacingHorizontalL,
    color: tokens.colorNeutralForeground2,
    fontSize: "0.875rem",
  },
  passwordRuleItem: {
    marginBottom: tokens.spacingVerticalXS,
  },
  passwordRuleItemPassed: {
    color: tokens.colorPaletteGreenForeground1,
  },
  passwordRuleItemFailed: {
    color: tokens.colorPaletteRedForeground1,
  },
});

// ---------------------------------------------------------------------------
// Form state
// ---------------------------------------------------------------------------

interface FormValues {
  masterPassword: string;
  confirmPassword: string;
  databasePath: string;
  fail2banSocket: string;
  timezone: string;
  sessionDurationMinutes: string;
}

const DEFAULT_VALUES: FormValues = {
  masterPassword: "",
  confirmPassword: "",
  databasePath: "bangui.db",
  fail2banSocket: "/var/run/fail2ban/fail2ban.sock",
  timezone: "UTC",
  sessionDurationMinutes: "60",
};

type ValidationErrors = Partial<Record<keyof FormValues, string>>;

type PasswordRuleId = "length" | "uppercase" | "number" | "special";

interface PasswordRule {
  id: PasswordRuleId;
  label: string;
  test: (password: string) => boolean;
}

const PASSWORD_RULES: PasswordRule[] = [
  {
    id: "length",
    label: "At least 8 characters",
    test: (password: string) => password.length >= 8,
  },
  {
    id: "uppercase",
    label: "At least one uppercase letter",
    test: (password: string) => /[A-Z]/.test(password),
  },
  {
    id: "number",
    label: "At least one number",
    test: (password: string) => /\d/.test(password),
  },
  {
    id: "special",
    label: "At least one special character (!@#$%^&*())",
    test: (password: string) => /[!@#$%^&*()]/.test(password),
  },
];

function getPasswordRuleStatus(password: string) {
  return PASSWORD_RULES.map((rule) => ({
    ...rule,
    satisfied: rule.test(password),
  }));
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

/**
 * First-run setup wizard page.
 * Collects master password and server preferences.
 */
export function SetupPage(): React.JSX.Element {
  const styles = useStyles();
  const navigate = useNavigate();

  const { status, loading, error, submit, submitting, submitError } = useSetup();
  const [values, setValues] = useState<FormValues>(DEFAULT_VALUES);
  const [errors, setErrors] = useState<ValidationErrors>({});
  const passwordRules = getPasswordRuleStatus(values.masterPassword);
  const passwordStrength = passwordRules.filter((rule) => rule.satisfied).length;
  const apiError = error ?? submitError;

  // Redirect to /login if setup has already been completed.
  // Show a full-screen spinner while the initial status check is in flight.
  useEffect(() => {
    if (status?.completed) {
      navigate("/login", { replace: true });
    }
  }, [navigate, status]);

  // ---------------------------------------------------------------------------
  // Handlers
  // ---------------------------------------------------------------------------

  function handleChange(field: keyof FormValues) {
    return (ev: ChangeEvent<HTMLInputElement>): void => {
      setValues((prev) => ({ ...prev, [field]: ev.target.value }));
      // Clear field-level error on change.
      setErrors((prev) => ({ ...prev, [field]: undefined }));
    };
  }

  function validate(): boolean {
    const next: ValidationErrors = {};
    const unmetPasswordRules = passwordRules.filter((rule) => !rule.satisfied);

    if (!values.masterPassword.trim()) {
      next.masterPassword = "Password is required.";
    } else if (unmetPasswordRules.length > 0) {
      next.masterPassword = "Password must meet all complexity requirements.";
    }

    if (values.masterPassword !== values.confirmPassword) {
      next.confirmPassword = "Passwords do not match.";
    }
    if (!values.databasePath.trim()) {
      next.databasePath = "Database path is required.";
    }
    if (!values.fail2banSocket.trim()) {
      next.fail2banSocket = "Socket path is required.";
    }
    const duration = parseInt(values.sessionDurationMinutes, 10);
    if (isNaN(duration) || duration < 1) {
      next.sessionDurationMinutes = "Session duration must be at least 1 minute.";
    }

    setErrors(next);
    return Object.keys(next).length === 0;
  }

  async function handleSubmit(ev: FormEvent<HTMLFormElement>): Promise<void> {
    ev.preventDefault();

    if (!validate()) return;

    try {
      await submit({
        master_password: values.masterPassword,
        database_path: values.databasePath,
        fail2ban_socket: values.fail2banSocket,
        timezone: values.timezone,
        session_duration_minutes: parseInt(values.sessionDurationMinutes, 10),
      });
      navigate("/login", { replace: true });
    } catch {
      // Errors are surfaced through the hook via `submitError`.
    }
  }

  // ---------------------------------------------------------------------------
  // Render
  // ---------------------------------------------------------------------------

  if (loading) {
    return (
      <div
        style={{
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          minHeight: "100vh",
        }}
      >
        <Spinner size="large" label="Loading…" />
      </div>
    );
  }

  return (
    <div className={styles.root}>
      <div className={styles.card}>
        <Text as="h1" size={700} weight="semibold" className={styles.heading}>
          BanGUI Setup
        </Text>
        <Text size={300} className={styles.description}>
          Configure BanGUI for first use. This page will not be shown again once setup
          is complete.
        </Text>

        {apiError && (
          <MessageBar intent="error" className={styles.error}>
            <MessageBarBody>{apiError}</MessageBarBody>
          </MessageBar>
        )}

        <form onSubmit={(ev) => void handleSubmit(ev)}>
          {/* Hidden username field — required by accessibility guidelines for
              password managers to correctly identify the credential form. */}
          <input
            type="text"
            autoComplete="username"
            aria-hidden="true"
            tabIndex={-1}
            value="bangui"
            readOnly
            style={{ display: "none" }}
          />
          <div className={styles.fields}>
            <Field
              label="Master Password"
              required
              validationMessage={
                errors.masterPassword ??
                (passwordRules.some((rule) => !rule.satisfied)
                  ? {
                      children: (
                        <ul className={styles.passwordRuleList}>
                          {passwordRules.map((rule) => (
                            <li
                              key={rule.id}
                              className={
                                rule.satisfied
                                  ? styles.passwordRuleItemPassed
                                  : styles.passwordRuleItemFailed
                              }
                            >
                              {rule.label}
                            </li>
                          ))}
                        </ul>
                      ),
                    }
                  : undefined)
              }
              validationState={
                errors.masterPassword || passwordRules.some((rule) => !rule.satisfied)
                  ? "error"
                  : "none"
              }
            >
              <Input
                type="password"
                value={values.masterPassword}
                onChange={handleChange("masterPassword")}
                autoComplete="new-password"
                aria-label="Master Password"
              />
              <div className={styles.passwordStrength} aria-live="polite">
                <div className={styles.passwordStrengthBar}>
                  {passwordRules.map((rule) => (
                    <span
                      key={rule.id}
                      className={
                        rule.satisfied
                          ? `${styles.passwordStrengthSegment} ${styles.passwordStrengthSegmentActive}`
                          : styles.passwordStrengthSegment
                      }
                    />
                  ))}
                </div>
                <Text size={200}>
                  {passwordStrength} of {passwordRules.length} rules satisfied
                </Text>
              </div>
            </Field>

            <Field
              label="Confirm Password"
              required
              validationMessage={errors.confirmPassword}
              validationState={errors.confirmPassword ? "error" : "none"}
            >
              <Input
                type="password"
                value={values.confirmPassword}
                onChange={handleChange("confirmPassword")}
                autoComplete="new-password"
                aria-label="Confirm Password"
              />
            </Field>

            <Field
              label="Database Path"
              hint="Path where BanGUI stores its SQLite database."
              validationMessage={errors.databasePath}
              validationState={errors.databasePath ? "error" : "none"}
            >
              <Input
                value={values.databasePath}
                onChange={handleChange("databasePath")}
                aria-label="Database Path"
              />
            </Field>

            <Field
              label="fail2ban Socket Path"
              hint="Unix socket used to communicate with the fail2ban daemon."
              validationMessage={errors.fail2banSocket}
              validationState={errors.fail2banSocket ? "error" : "none"}
            >
              <Input
                value={values.fail2banSocket}
                onChange={handleChange("fail2banSocket")}
                aria-label="fail2ban Socket Path"
              />
            </Field>

            <Field
              label="Timezone"
              hint="IANA timezone identifier (e.g. UTC, Europe/Berlin)."
            >
              <Input
                value={values.timezone}
                onChange={handleChange("timezone")}
                aria-label="Timezone"
              />
            </Field>

            <Field
              label="Session Duration (minutes)"
              hint="How long a login session stays active."
              validationMessage={errors.sessionDurationMinutes}
              validationState={errors.sessionDurationMinutes ? "error" : "none"}
            >
              <Input
                type="number"
                value={values.sessionDurationMinutes}
                onChange={handleChange("sessionDurationMinutes")}
                min={1}
                aria-label="Session Duration (minutes)"
              />
            </Field>
          </div>

          <div className={styles.submitRow}>
            <Button
              type="submit"
              appearance="primary"
              disabled={submitting}
              icon={submitting ? <Spinner size="tiny" /> : undefined}
            >
              {submitting ? "Saving…" : "Complete Setup"}
            </Button>
          </div>
        </form>
      </div>
    </div>
  );
}
