import { onMounted, reactive, ref, watch } from "vue";

/**
 * Build the Request Access page state machine.
 *
 * WHY: the view combines form submission, verification-link handling,
 * provisioning retry, and status polling; keeping that orchestration in a
 * composable makes the SFC small and gives the behavior a testable seam.
 *
 * @param {import("vue-router").RouteLocationNormalizedLoaded} route - active route with optional verification query params.
 * @returns {object} reactive state and event handlers used by the view template.
 */
export function useRequestAccessFlow(route) {
  /**
   * Convert backend request status into copy for the public flow.
   *
   * @param {string} value - Raw access request status.
   * @returns {string} Human-readable status label.
   */
  function statusLabel(value) {
    const key = (value || "").trim();
    if (key === "pending_email_verification") return "confirm email";
    if (key === "pending") return "awaiting approval";
    if (key === "accounts_building") return "accounts building";
    if (key === "awaiting_onboarding") return "awaiting onboarding";
    if (key === "ready") return "ready";
    if (key === "denied") return "rejected";
    return key || "unknown";
  }

  /**
   * Convert backend request status into the matching pill class.
   *
   * @param {string} value - Raw access request status.
   * @returns {string} CSS class for status emphasis.
   */
  function statusPillClass(value) {
    const key = (value || "").trim();
    if (key === "pending_email_verification") return "pill-warn";
    if (key === "pending") return "pill-wait";
    if (key === "accounts_building") return "pill-warn";
    if (key === "awaiting_onboarding") return "pill-ok";
    if (key === "ready") return "pill-info";
    if (key === "denied") return "pill-bad";
    return "pill-warn";
  }

  const form = reactive({
    username: "",
    first_name: "",
    last_name: "",
    email: "",
    note: "",
  });

  const submitting = ref(false);
  const submitted = ref(false);
  const error = ref("");
  const requestCode = ref("");
  const copied = ref(false);
  const verifying = ref(false);
  const mailDomain = import.meta.env?.VITE_MAILU_DOMAIN || "bstein.dev";
  const availability = reactive({
    label: "",
    detail: "",
    pillClass: "",
    checking: false,
    blockSubmit: false,
  });
  let availabilityTimer = 0;
  let availabilityToken = 0;

  const statusForm = reactive({
    request_code: "",
  });
  const checking = ref(false);
  const status = ref("");
  const onboardingUrl = ref("");
  const tasks = ref([]);
  const blocked = ref(false);
  const retrying = ref(false);
  const retryMessage = ref("");
  const resending = ref(false);
  const resendMessage = ref("");
  const verifyBanner = ref(null);

  function taskPillClass(status) {
    const key = (status || "").trim();
    if (key === "ok") return "pill-ok";
    if (key === "error") return "pill-bad";
    if (key === "pending") return "pill-warn";
    return "pill-warn";
  }

  function resetAvailability() {
    availability.label = "";
    availability.detail = "";
    availability.pillClass = "";
    availability.blockSubmit = false;
  }

  /**
   * Update username availability UI state from a normalized backend result.
   *
   * @param {string} state - Availability state key.
   * @param {string} detail - Optional human-readable explanation.
   * @returns {void}
   */
  function setAvailability(state, detail = "") {
    availability.detail = detail;
    availability.blockSubmit = false;
    if (state === "checking") {
      availability.label = "checking";
      availability.pillClass = "pill-warn";
      return;
    }
    if (state === "available") {
      availability.label = "available";
      availability.pillClass = "pill-ok";
      return;
    }
    if (state === "invalid") {
      availability.label = "invalid";
      availability.pillClass = "pill-bad";
      availability.blockSubmit = true;
      return;
    }
    if (state === "requested") {
      availability.label = "requested";
      availability.pillClass = "pill-warn";
      availability.blockSubmit = true;
      return;
    }
    if (state === "exists") {
      availability.label = "taken";
      availability.pillClass = "pill-bad";
      availability.blockSubmit = true;
      return;
    }
    if (state === "error") {
      availability.label = "error";
      availability.pillClass = "pill-warn";
      return;
    }
    resetAvailability();
  }

  async function checkAvailability(name) {
    const token = (availabilityToken += 1);
    setAvailability("checking");
    availability.checking = true;
    try {
      const resp = await fetch(`/api/access/request/availability?username=${encodeURIComponent(name)}`, {
        headers: { Accept: "application/json" },
        cache: "no-store",
      });
      const data = await resp.json().catch(() => ({}));
      if (token !== availabilityToken) return;
      if (!resp.ok) throw new Error(data.error || `status ${resp.status}`);
      if (data.available) {
        setAvailability("available", "Username is available.");
        return;
      }
      const reason = data.reason || "";
      const status = data.status || "";
      if (reason === "invalid") {
        setAvailability("invalid", data.detail || "Use 3-32 characters (letters, numbers, . _ -).");
        return;
      }
      if (reason === "exists") {
        setAvailability("exists", "Already in use. Choose another name.");
        return;
      }
      if (reason === "requested") {
        const label = status ? `Existing request: ${statusLabel(status)}` : "Request already exists.";
        setAvailability("requested", label);
        return;
      }
      setAvailability("error", "Unable to confirm availability.");
    } catch (err) {
      if (token !== availabilityToken) return;
      setAvailability("error", err.message || "Availability check failed.");
    } finally {
      if (token === availabilityToken) availability.checking = false;
    }
  }

  async function submit() {
    if (submitting.value) return;
    error.value = "";
    submitting.value = true;
    try {
      const resp = await fetch("/api/access/request", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        cache: "no-store",
        body: JSON.stringify({
          username: form.username.trim(),
          first_name: form.first_name.trim(),
          last_name: form.last_name.trim(),
          email: form.email.trim(),
          note: form.note.trim(),
        }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) throw new Error(data.error || resp.statusText || `status ${resp.status}`);
      submitted.value = true;
      requestCode.value = data.request_code || "";
      statusForm.request_code = requestCode.value;
      status.value = data.status || "pending_email_verification";
    } catch (err) {
      error.value = err.message || "Failed to submit request";
    } finally {
      submitting.value = false;
    }
  }

  watch(
    () => form.username,
    (value) => {
      const trimmed = value.trim();
      if (availabilityTimer) {
        window.clearTimeout(availabilityTimer);
        availabilityTimer = 0;
      }
      availabilityToken += 1;
      if (!trimmed) {
        resetAvailability();
        return;
      }
      if (trimmed.length < 3 || trimmed.length > 32) {
        setAvailability("invalid", "Use 3-32 characters (letters, numbers, . _ -).");
        return;
      }
      if (!/^[a-zA-Z0-9._-]+$/.test(trimmed)) {
        setAvailability("invalid", "Use letters, numbers, and . _ - only.");
        return;
      }
      availabilityTimer = window.setTimeout(() => {
        checkAvailability(trimmed);
      }, 350);
    },
  );

  async function copyRequestCode() {
    if (!requestCode.value) return;
    try {
      if (navigator?.clipboard?.writeText) {
        await navigator.clipboard.writeText(requestCode.value);
      } else {
        const textarea = document.createElement("textarea");
        textarea.value = requestCode.value;
        textarea.setAttribute("readonly", "");
        textarea.style.position = "fixed";
        textarea.style.top = "-9999px";
        textarea.style.left = "-9999px";
        document.body.appendChild(textarea);
        textarea.select();
        textarea.setSelectionRange(0, textarea.value.length);
        document.execCommand("copy");
        document.body.removeChild(textarea);
      }
      copied.value = true;
      setTimeout(() => (copied.value = false), 1500);
    } catch (err) {
      error.value = err?.message || "Failed to copy request code";
    }
  }

  async function checkStatus() {
    if (checking.value) return;
    error.value = "";
    verifyBanner.value = null;
    const trimmed = statusForm.request_code.trim();
    if (!trimmed) return;
    if (!trimmed.includes("~")) {
      error.value = "Request code should look like username~XXXXXXXXXX. Copy it from the submit step.";
      status.value = "unknown";
      onboardingUrl.value = "";
      tasks.value = [];
      blocked.value = false;
      return;
    }
    checking.value = true;
    try {
      const resp = await fetch("/api/access/request/status", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        cache: "no-store",
        body: JSON.stringify({ request_code: trimmed }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) throw new Error(data.error || resp.statusText || `status ${resp.status}`);
      status.value = data.status || "unknown";
      onboardingUrl.value = data.onboarding_url || "";
      tasks.value = Array.isArray(data.tasks) ? data.tasks : [];
      blocked.value = Boolean(data.blocked);
      if (data.email_verified && status.value === "pending") {
        verifyBanner.value = {
          title: "Email confirmed",
          body: "Your request is now waiting for manual approval. Check back here after an admin reviews it.",
        };
      } else {
        verifyBanner.value = null;
      }
    } catch (err) {
      error.value = err.message || "Failed to check status";
      status.value = "unknown";
      onboardingUrl.value = "";
      tasks.value = [];
      blocked.value = false;
    } finally {
      checking.value = false;
    }
  }

  async function retryProvisioning() {
    if (retrying.value) return;
    retryMessage.value = "";
    const code = statusForm.request_code.trim();
    if (!code) return;
    retrying.value = true;
    try {
      const retryTasks = tasks.value
        .filter((item) => item.status === "error")
        .map((item) => item.task)
        .filter(Boolean);
      const resp = await fetch("/api/access/request/retry", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        cache: "no-store",
        body: JSON.stringify({ request_code: code, tasks: retryTasks }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) throw new Error(data.error || resp.statusText || `status ${resp.status}`);
      retryMessage.value = "Retry requested. Check again in a moment.";
      await checkStatus();
    } catch (err) {
      retryMessage.value = err?.message || "Retry request failed.";
    } finally {
      retrying.value = false;
    }
  }

  async function verifyFromLink(code, token) {
    verifying.value = true;
    try {
      const resp = await fetch("/api/access/request/verify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        cache: "no-store",
        body: JSON.stringify({ request_code: code, token }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) throw new Error(data.error || resp.statusText || `status ${resp.status}`);
      status.value = data.status || status.value;
      if (status.value === "pending") {
        verifyBanner.value = {
          title: "Email confirmed",
          body: "Your request is now waiting for manual approval. Check back here after an admin reviews it.",
        };
      } else {
        verifyBanner.value = null;
      }
    } finally {
      verifying.value = false;
    }
  }

  async function resendVerification() {
    if (resending.value) return;
    const code = statusForm.request_code.trim();
    if (!code) return;
    resending.value = true;
    resendMessage.value = "";
    try {
      const resp = await fetch("/api/access/request/resend", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        cache: "no-store",
        body: JSON.stringify({ request_code: code }),
      });
      const data = await resp.json().catch(() => ({}));
      if (!resp.ok) throw new Error(data.error || resp.statusText || `status ${resp.status}`);
      resendMessage.value = "Verification email sent.";
    } catch (err) {
      resendMessage.value = err?.message || "Failed to resend verification email.";
    } finally {
      resending.value = false;
    }
  }

  onMounted(async () => {
    const code = typeof route.query.code === "string" ? route.query.code.trim() : "";
    const token = typeof route.query.verify === "string" ? route.query.verify.trim() : "";
    const verified = typeof route.query.verified === "string" ? route.query.verified.trim() : "";
    const verifyError = typeof route.query.verify_error === "string" ? route.query.verify_error.trim() : "";
    if (code) {
      requestCode.value = code;
      statusForm.request_code = code;
      submitted.value = true;
    }
    if (code && token) {
      try {
        await verifyFromLink(code, token);
      } catch (err) {
        error.value = err?.message || "Failed to verify email";
      }
    }
    if (code) {
      await checkStatus();
    }
    if (verified && status.value === "pending") {
      verifyBanner.value = {
        title: "Email confirmed",
        body: "Your request is now waiting for manual approval. Check back here after an admin reviews it.",
      };
    }
    if (verifyError) {
      error.value = `Email verification failed: ${decodeURIComponent(verifyError)}`;
    }
  });

  return {
    statusLabel,
    statusPillClass,
    form,
    submitting,
    submitted,
    error,
    requestCode,
    copied,
    verifying,
    mailDomain,
    availability,
    statusForm,
    checking,
    status,
    onboardingUrl,
    tasks,
    blocked,
    retrying,
    retryMessage,
    resending,
    resendMessage,
    verifyBanner,
    taskPillClass,
    submit,
    copyRequestCode,
    checkStatus,
    retryProvisioning,
    resendVerification,
  };
}
