import { Alpine } from "./alpine";
import { applyTheme } from "./theme";
import { setToken, setRefreshToken, getToken, setSelectedLibrary } from "./storage";
import {
  apiPost,
  apiGet,
  apiPut,
  apiDelete,
  handleResponse,
  handleVoidResponse,
  handleError,
} from "./api";
import { showToast } from "./toast";
import { initPasswordValidation } from "./password_validation";

interface LibraryEntry {
  id: string;
  name: string;
  type: string;
  description: string;
  folders: string[];
}

interface ScanJob {
  jobId: string;
  libraryId: string;
  libraryName: string;
  progress: number;
  statusText: string;
}

let scanPollInterval: ReturnType<typeof setInterval> | undefined;

function initSetup(): void {
  const savedTheme = localStorage.getItem("theme");
  if (savedTheme && savedTheme !== "tokyo-night") {
    applyTheme(savedTheme);
  }
}Alpine.data("setupWizard", () => ({
  currentStep: 1 as number,
  loading: false as boolean,
  errorMsg: "" as string,

  admin: {
    baseUrl: "",
    email: "",
    username: "",
    password: "",
    confirmPassword: "",
    firstName: "",
    lastName: "",
  },

  libraries: [] as LibraryEntry[],
  newLibrary: {
    name: "",
    type: "ebooks",
    description: "",
  },

  scanStarted: false as boolean,
  scanComplete: false as boolean,
  scanJobs: [] as ScanJob[],

  // Folder browser state
  currentBrowsePath: "" as string,
  currentBrowseInputId: "" as string,

  initSetup,

  init() {
    initSetup();
    this.admin.baseUrl = window.location.origin;
    setTimeout(() => {
      initPasswordValidation();
    }, 100);

    const modal = document.getElementById("folder-browser-modal");
    if (modal) {
      modal.addEventListener("click", (e: Event) => this.handleBrowseClick(e));
    }
  },

  clearError() {
    this.errorMsg = "";
  },

  async submitAdmin() {
    this.clearError();
    this.loading = true;

    try {
      const response = await fetch("/api/auth/register", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          email: this.admin.email,
          username: this.admin.username,
          password: this.admin.password,
          first_name: this.admin.firstName,
          last_name: this.admin.lastName,
        }),
      });

      if (!response.ok) {
        let errMsg = `Registration failed (${response.status})`;
        try {
          const errData = await response.clone().json();
          errMsg = errData.error || errMsg;
        } catch {
          // Can't parse body
        }

        if (
          (errMsg.includes("already exists") || errMsg.includes("email already") || errMsg.includes("username already")) &&
          getToken()
        ) {
          this.currentStep = 2;
          this.errorMsg = "";
          return;
        }

        this.errorMsg = errMsg;
        return;
      }

      const data = await response.json();
      setToken(data.token || data.access_token);
      setRefreshToken(data.refresh_token);

      await this.saveBaseUrl();

      showToast("Admin account created successfully!", "success");
      this.currentStep = 2;
    } catch (err) {
      this.errorMsg = (err as Error).message || "Network error during registration";
    } finally {
      this.loading = false;
    }
  },

  async saveBaseUrl() {
    if (!this.admin.baseUrl) return;
    try {
      await handleVoidResponse(
        await apiPut("/system/config", { base_url: this.admin.baseUrl })
      );
    } catch {
      showToast("Warning: could not save server URL. You can set it later in Admin Settings.", "warning");
    }
  },

  async addLibrary() {
    this.clearError();
    this.loading = true;

    try {
      const response = await apiPost("/libraries", {
        name: this.newLibrary.name,
        type: this.newLibrary.type,
        description: this.newLibrary.description,
      });
      const result = (await handleResponse(response)) as { id: string; name: string };

      this.libraries.push({
        id: result.id,
        name: result.name,
        type: this.newLibrary.type,
        description: this.newLibrary.description,
        folders: [],
      });

      this.newLibrary = { name: "", type: "ebooks", description: "" };
      showToast("Library created", "success");
    } catch (err) {
      handleError(err, "Failed to create library");
    } finally {
      this.loading = false;
    }
  },

  async removeLibrary(idx: number) {
    const lib = this.libraries[idx];
    if (!lib) return;
    if (!confirm(`Remove library "${lib.name}"?`)) return;

    try {
      const response = await apiDelete(`/libraries/${lib.id}`);
      await handleVoidResponse(response);
      this.libraries.splice(idx, 1);
      showToast("Library removed", "success");
    } catch (err) {
      handleError(err, "Failed to remove library");
    }
  },

  async addFolderToLibrary(lib: LibraryEntry) {
    const inputId = `folderInput-${lib.id}`;
    const input = document.getElementById(inputId) as HTMLInputElement;
    if (!input) return;

    const folderPath = input.value.trim();
    if (!folderPath) return;

    this.clearError();
    this.loading = true;

    try {
      const response = await apiPost(`/libraries/${lib.id}/folders`, {
        folder_path: folderPath,
      });
      await handleVoidResponse(response);

      if (!lib.folders) lib.folders = [];
      lib.folders.push(folderPath);
      input.value = "";
      showToast("Folder added", "success");
    } catch (err) {
      handleError(err, "Failed to add folder");
    } finally {
      this.loading = false;
    }
  },

  async removeFolder(libraryId: string, folderPath: string, index: number) {
    if (!confirm(`Remove folder "${folderPath}"?`)) return;

    try {
      const response = await apiDelete(`/libraries/${libraryId}/folders`, {
        folder_path: folderPath,
      });
      await handleVoidResponse(response);

      const lib = this.libraries.find((l) => l.id === libraryId);
      if (lib && lib.folders) {
        lib.folders.splice(index, 1);
      }
      showToast("Folder removed", "success");
    } catch (err) {
      handleError(err, "Failed to remove folder");
    }
  },

  // Folder browser
  showFolderBrowser(inputId: string) {
    this.currentBrowseInputId = inputId;
    this.currentBrowsePath = "/";
    const modal = document.getElementById("folder-browser-modal");
    if (modal) {
      modal.classList.remove("hidden");
      this.loadBrowseDirectories("/");
    }
  },

  hideFolderBrowser() {
    const modal = document.getElementById("folder-browser-modal");
    if (modal) {
      modal.classList.add("hidden");
    }
  },

  async loadBrowseDirectories(path: string) {
    try {
      const response = await apiGet(`/libraries/browse?path=${encodeURIComponent(path)}`);
      const data = (await handleResponse(response)) as {
        current_path: string;
        parent_path: string;
        directories: string[];
      };
      this.currentBrowsePath = data.current_path;
      this.renderBrowseDirectories(data);
    } catch (err) {
      handleError(err, "Failed to load directories");
    }
  },

  escapeHtml(text: string): string {
    const div = document.createElement("div");
    div.textContent = text;
    return div.innerHTML;
  },

  renderBrowseDirectories(data: {
    current_path: string;
    parent_path: string;
    directories: string[];
  }) {
    const container = document.getElementById("folder-browser-content");
    if (!container) return;

    let html = `
      <div class="flex items-center gap-2 mb-4">
        ${
          data.parent_path
            ? `<button type="button" data-action="browse-parent" data-path="${this.escapeHtml(data.parent_path)}" class="btn-secondary px-3 py-1 rounded">↑ Parent</button>`
            : ""
        }
        <span class="text-sm" style="color: var(--text-secondary)">${this.escapeHtml(data.current_path)}</span>
      </div>
      <div class="max-h-64 overflow-y-auto space-y-1">
    `;

    if (data.directories.length === 0) {
      html += '<p style="color: var(--text-secondary)" class="text-center py-4">No subdirectories</p>';
    } else {
      data.directories.forEach((dir) => {
        const fullPath = data.current_path === "/" ? `/${dir}` : `${data.current_path}/${dir}`;
        html += `
          <div class="p-2 rounded cursor-pointer hover:opacity-80"
               style="background-color: var(--bg-primary); color: var(--text-primary)"
               data-action="browse-navigate"
               data-path="${this.escapeHtml(fullPath)}">
            📁 ${this.escapeHtml(dir)}
          </div>
        `;
      });
    }

    html += `
      </div>
      <div class="mt-4 flex justify-end gap-2">
        <button type="button" data-action="browse-cancel" class="btn-secondary px-4 py-2 rounded">Cancel</button>
        <button type="button" data-action="browse-select" data-path="${this.escapeHtml(data.current_path)}" class="btn-primary px-4 py-2 rounded">Select This Folder</button>
      </div>
    `;

    container.innerHTML = html;
  },

  handleBrowseClick(e: Event) {
    const target = e.target as HTMLElement;
    const button = target.closest("button") as HTMLElement;
    const div = target.closest("div[data-action]") as HTMLElement;

    if (button) {
      const action = button.dataset.action;
      const path = button.dataset.path;

      if (action === "browse-parent" && path) {
        this.loadBrowseDirectories(path);
      } else if (action === "browse-cancel") {
        this.hideFolderBrowser();
      } else if (action === "browse-select" && path) {
        this.selectBrowseFolder(path);
      }
    }

    if (div && div.dataset.action === "browse-navigate") {
      const path = div.dataset.path;
      if (path) this.loadBrowseDirectories(path);
    }
  },

  selectBrowseFolder(path: string) {
    if (this.currentBrowseInputId) {
      const input = document.getElementById(this.currentBrowseInputId) as HTMLInputElement;
      if (input) {
        input.value = path;
      }
    }
    this.hideFolderBrowser();
  },

  async startScan() {
    this.clearError();
    this.loading = true;
    this.scanStarted = true;

    if (this.libraries.length === 0) {
      this.scanComplete = true;
      this.loading = false;
      return;
    }

    try {
      this.scanJobs = [];

      for (const lib of this.libraries) {
        const response = await apiPost(`/libraries/${lib.id}/scan`, { force: true });
        if (response.ok) {
          const result = await response.json();
          this.scanJobs.push({
            jobId: result.job_id,
            libraryId: lib.id,
            libraryName: lib.name,
            progress: 0,
            statusText: "Pending...",
          });
        } else {
          this.scanJobs.push({
            jobId: "",
            libraryId: lib.id,
            libraryName: lib.name,
            progress: 0,
            statusText: "Failed to start",
          });
        }
      }

      if (this.scanJobs.length === 0) {
        this.errorMsg = "Failed to start any scans";
        return;
      }

      showToast("Scan started", "success");
      this.pollScanProgress();
    } catch (err) {
      this.errorMsg = (err as Error).message;
      showToast("Failed to start scan", "error");
    } finally {
      this.loading = false;
    }
  },

  pollScanProgress() {
    if (scanPollInterval !== undefined) {
      clearInterval(scanPollInterval);
    }

    scanPollInterval = setInterval(async () => {
      let allComplete = true;

      for (const job of this.scanJobs) {
        if (!job.jobId) continue;

        try {
          const response = await apiGet(`/scanner/status/${job.jobId}`);
          if (response.ok) {
            const status = await response.json();
            job.progress = Math.round((status.progress || 0) * 100);

            const statusMessages: Record<string, string> = {
              pending: "Pending...",
              running: `Scanning... ${Math.round((status.progress || 0) * 100)}%`,
              completed: `✓ Complete (${status.new_items || 0} items)`,
              failed: "✗ Failed",
            };
            job.statusText = statusMessages[status.status] || status.status;

            if (status.status !== "completed" && status.status !== "failed") {
              allComplete = false;
            }
          } else {
            allComplete = false;
          }
        } catch {
          allComplete = false;
        }
      }

      if (allComplete) {
        clearInterval(scanPollInterval);
        scanPollInterval = undefined;
        this.scanComplete = true;
        showToast("Scan complete!", "success");
      }
    }, 2000);
  },

  async finishSetup() {
    this.loading = true;
    try {
      // Setup completion is derived from the existence of an admin user, so
      // there is no separate "complete" endpoint to call. The admin account
      // created in submitAdmin already marks setup as done server-side.
      if (this.libraries.length > 0) {
        setSelectedLibrary(this.libraries[0].id);
      }
      window.location.href = "/dashboard";
    } finally {
      this.loading = false;
    }
  },
}));
