import type { CSSProperties } from "react";
import { useEffect, useState } from "react";
import { openWorldshaperStudioWindow } from "./worldshaperStudio/windowing";
import {
  CHANGELOG_SECTIONS,
  CHANGELOG_SPLASH_FOOTNOTE,
  CHANGELOG_SPLASH_KICKER,
  CHANGELOG_SPLASH_TITLE,
  CHANGELOG_SPLASH_VERSION,
} from "./worldshaperStudio/changelogData";
import type { ChangelogItem } from "./worldshaperStudio/changelogData";
import launcherBackground from "../background.png";

type WorldDefaultPayload = {
  worldId?: string;
  world?: {
    id?: string;
  };
};

type LaunchState = "ready" | "opening" | "opened" | "blocked" | "error";
type BoardTab = "news" | "requests";

type LauncherRequest = {
  id: string;
  text: string;
  done: boolean;
  createdAt: string;
  updatedAt: string;
};

type LauncherRequestsPayload = {
  requests?: LauncherRequest[];
};

const DEFAULT_EDITOR_WORLD_ID_FALLBACK = "overworld";

async function resolveDefaultWorldId(): Promise<string> {
  const response = await fetch("/api/world-default");
  if (!response.ok) {
    throw new Error(`Failed to load default world (${response.status}).`);
  }
  const payload = await response.json() as WorldDefaultPayload;
  const resolvedWorldId = String(payload.worldId || payload.world?.id || "").trim();
  return resolvedWorldId || DEFAULT_EDITOR_WORLD_ID_FALLBACK;
}

async function fetchJsonOrThrow<T>(input: RequestInfo | URL, init?: RequestInit): Promise<T> {
  const response = await fetch(input, init);
  if (!response.ok) {
    let detail = `Request failed (${response.status}).`;
    try {
      const payload = await response.json() as { error?: string };
      detail = String(payload?.error || detail);
    } catch {
      // Ignore JSON parse failures and fall back to status text.
    }
    throw new Error(detail);
  }
  return response.json() as Promise<T>;
}

function formatRequestTimestamp(value: string): string {
  const parsed = Date.parse(String(value || ""));
  if (!Number.isFinite(parsed)) {
    return "Saved recently";
  }
  return new Intl.DateTimeFormat(undefined, {
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit",
  }).format(parsed);
}

function openStudioPopup(worldId: string): boolean {
  const popup = openWorldshaperStudioWindow(worldId, window, { worldId });
  return Boolean(popup);
}

function openRepo(): void {
  window.location.assign("https://repo.andraxion.net/");
}

function WorldshaperLauncher() {
  const [launchState, setLaunchState] = useState<LaunchState>("ready");
  const [status, setStatus] = useState("Launch Worldshaper Studio in its floating window.");
  const [error, setError] = useState("");
  const [worldId, setWorldId] = useState(DEFAULT_EDITOR_WORLD_ID_FALLBACK);
  const [activeBoardTab, setActiveBoardTab] = useState<BoardTab>("news");
  const [requests, setRequests] = useState<LauncherRequest[]>([]);
  const [requestsLoading, setRequestsLoading] = useState(true);
  const [requestsError, setRequestsError] = useState("");
  const [requestDraftOpen, setRequestDraftOpen] = useState(false);
  const [requestDraft, setRequestDraft] = useState("");
  const [requestSubmitting, setRequestSubmitting] = useState(false);
  const [requestMutatingId, setRequestMutatingId] = useState("");

  useEffect(() => {
    let cancelled = false;
    void resolveDefaultWorldId()
      .then((resolvedWorldId) => {
        if (cancelled) {
          return;
        }
        setWorldId(resolvedWorldId);
      })
      .catch(() => {
        if (cancelled) {
          return;
        }
        setWorldId(DEFAULT_EDITOR_WORLD_ID_FALLBACK);
      });

    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    let cancelled = false;

    async function loadRequests() {
      setRequestsLoading(true);
      try {
        const payload = await fetchJsonOrThrow<LauncherRequestsPayload>("/api/launcher-requests");
        if (cancelled) {
          return;
        }
        setRequests(Array.isArray(payload.requests) ? payload.requests : []);
        setRequestsError("");
      } catch (nextError: unknown) {
        if (cancelled) {
          return;
        }
        setRequestsError(String(nextError || "Failed to load requests."));
      } finally {
        if (!cancelled) {
          setRequestsLoading(false);
        }
      }
    }

    void loadRequests();

    return () => {
      cancelled = true;
    };
  }, []);

  async function handleLaunch(): Promise<void> {
    setError("");
    const nextWorldId = worldId || DEFAULT_EDITOR_WORLD_ID_FALLBACK;
    setLaunchState("opening");
    setStatus(`Opening Worldshaper Studio for ${nextWorldId}...`);
    try {
      const resolvedWorldId = nextWorldId || await resolveDefaultWorldId().catch(() => DEFAULT_EDITOR_WORLD_ID_FALLBACK);
      setWorldId(resolvedWorldId);
      setStatus(`Opening Worldshaper Studio for ${resolvedWorldId}...`);
      if (openStudioPopup(resolvedWorldId)) {
        setLaunchState("opened");
        setStatus("Worldshaper Studio opened in a separate window.");
        return;
      }
      setLaunchState("blocked");
      setStatus("Your browser blocked the studio window. Use the launch button again after allowing popups.");
    } catch (nextError: unknown) {
      const nextErrorText = String(nextError || "Failed to prepare Worldshaper Studio.");
      setLaunchState("error");
      setError(nextErrorText);
      setStatus("Worldshaper Studio unavailable.");
    }
  }

  async function handleAddRequest(): Promise<void> {
    const text = requestDraft.trim();
    if (!text) {
      setRequestsError("Write a request before saving it.");
      return;
    }
    setRequestSubmitting(true);
    try {
      const payload = await fetchJsonOrThrow<LauncherRequestsPayload>("/api/launcher-requests", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ text }),
      });
      setRequests(Array.isArray(payload.requests) ? payload.requests : []);
      setRequestDraft("");
      setRequestDraftOpen(false);
      setRequestsError("");
    } catch (nextError: unknown) {
      setRequestsError(String(nextError || "Failed to save request."));
    } finally {
      setRequestSubmitting(false);
    }
  }

  async function handleToggleRequest(requestEntry: LauncherRequest): Promise<void> {
    setRequestMutatingId(requestEntry.id);
    try {
      const payload = await fetchJsonOrThrow<LauncherRequestsPayload>(`/api/launcher-requests/${encodeURIComponent(requestEntry.id)}`, {
        method: "PATCH",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ done: !requestEntry.done }),
      });
      setRequests(Array.isArray(payload.requests) ? payload.requests : []);
      setRequestsError("");
    } catch (nextError: unknown) {
      setRequestsError(String(nextError || "Failed to update request."));
    } finally {
      setRequestMutatingId("");
    }
  }

  async function handleDeleteRequest(requestEntry: LauncherRequest): Promise<void> {
    const confirmed = window.confirm(`Delete this request?\n\n${requestEntry.text}`);
    if (!confirmed) {
      return;
    }
    setRequestMutatingId(requestEntry.id);
    try {
      const payload = await fetchJsonOrThrow<LauncherRequestsPayload>(`/api/launcher-requests/${encodeURIComponent(requestEntry.id)}`, {
        method: "DELETE",
      });
      setRequests(Array.isArray(payload.requests) ? payload.requests : []);
      setRequestsError("");
    } catch (nextError: unknown) {
      setRequestsError(String(nextError || "Failed to delete request."));
    } finally {
      setRequestMutatingId("");
    }
  }

  const isBusy = launchState === "opening";
  const requestCount = requests.length;
  const pendingRequestCount = requests.filter((entry) => entry.done !== true).length;
  const boardHint = activeBoardTab === "news"
    ? "Latest announcements"
    : `${pendingRequestCount} open request${pendingRequestCount === 1 ? "" : "s"}`;

  return (
    <main
      className="launcher-shell"
      style={{ "--launcher-background-image": `url(${launcherBackground})` } as CSSProperties}
    >
      <div className="launcher-stack">
        <section className="launcher-hero-window" aria-labelledby="launcher-studio-title">
          <div className="launcher-changelog-titlebar">
            <div className="launcher-changelog-title">Worldshaper Studio</div>
            <div className="launcher-changelog-hint">Floating editor launch</div>
          </div>
          <div className="launcher-hero-body">
            <div className="launcher-hero-card">
              <div className="changelog-splash-hero launcher-hero-banner">
                <p className="launcher-eyebrow">New RPG</p>
                <h1 className="launcher-title" id="launcher-studio-title">Worldshaper Studio</h1>
                <p className="launcher-status">{status}</p>
              </div>
              <div className="launcher-hero-support">
                {launchState === "blocked" ? (
                  <p className="launcher-hint">
                    Allow the popup, then use the studio button again to launch the floating editor window.
                  </p>
                ) : null}
                {launchState === "opened" ? (
                  <p className="launcher-hint">
                    The studio is open in its own slim window. This page stays behind as your release board and relaunch point.
                  </p>
                ) : null}
                {launchState === "ready" ? (
                  <p className="launcher-hint">
                    The editor is designed to live in its own floating window, so the launcher keeps the first step clean.
                  </p>
                ) : null}
                {error ? <p className="launcher-error">{error}</p> : null}
                <div className="launcher-actions">
                  <button type="button" className="launcher-primary-btn" onClick={() => void handleLaunch()} disabled={isBusy}>
                    Open Floating Studio
                  </button>
                  <button type="button" className="launcher-secondary-btn" onClick={openRepo} disabled={isBusy}>
                    Open Repo
                  </button>
                </div>
              </div>
            </div>
          </div>
        </section>
        <section className="launcher-changelog-window" aria-labelledby="launcher-board-title">
          <div className="launcher-changelog-titlebar">
            <div className="launcher-changelog-title" id="launcher-board-title">Worldshaper Board</div>
            <div className="launcher-changelog-hint">{boardHint}</div>
          </div>
          <div className="launcher-changelog-body">
            <div className="launcher-board-content">
              <div className="launcher-board-tabs">
                <button
                  type="button"
                  className={`launcher-secondary-btn launcher-board-tab ${activeBoardTab === "news" ? "is-active" : ""}`}
                  onClick={() => setActiveBoardTab("news")}
                >
                  News
                </button>
                <button
                  type="button"
                  className={`launcher-secondary-btn launcher-board-tab ${activeBoardTab === "requests" ? "is-active" : ""}`}
                  onClick={() => setActiveBoardTab("requests")}
                >
                  Requests
                </button>
              </div>
              {activeBoardTab === "news" ? (
                <div className="changelog-splash-card">
                  <div className="changelog-splash-hero">
                    <div className="changelog-splash-kicker">{CHANGELOG_SPLASH_KICKER}</div>
                    <div className="changelog-splash-title" id="launcher-whats-new-title">{CHANGELOG_SPLASH_TITLE}</div>
                    <div className="changelog-splash-meta">Release {CHANGELOG_SPLASH_VERSION}</div>
                  </div>
                  <div className="changelog-splash-list">
                    {CHANGELOG_SECTIONS.map((section) => (
                      <section key={section.title} className="changelog-splash-section">
                        <h3 className="changelog-splash-section-title">{section.title}</h3>
                        <ul className="changelog-splash-bullets">
                          {section.items.map((item, index) => {
                            const key = `${section.title}-${index}`;
                            const normalizedItem: ChangelogItem = item;
                            if (typeof normalizedItem === "string") {
                              return <li key={key}>{normalizedItem}</li>;
                            }
                            return (
                              <li key={key}>
                                <div>{normalizedItem.text}</div>
                                {normalizedItem.note ? <div className="changelog-splash-bullet-note">{normalizedItem.note}</div> : null}
                              </li>
                            );
                          })}
                        </ul>
                      </section>
                    ))}
                  </div>
                  <div className="changelog-splash-footer">
                    <div className="changelog-splash-footnote">{CHANGELOG_SPLASH_FOOTNOTE}</div>
                  </div>
                </div>
              ) : (
                <div className="changelog-splash-card">
                  <div className="changelog-splash-hero">
                    <div className="changelog-splash-kicker">Shared Request Board</div>
                    <div className="changelog-splash-title" id="launcher-requests-title">Requests</div>
                    <div className="changelog-splash-meta">
                      {requestCount} saved request{requestCount === 1 ? "" : "s"} with {pendingRequestCount} still open.
                    </div>
                  </div>
                  <div className="launcher-request-toolbar">
                    <button
                      type="button"
                      className="launcher-primary-btn"
                      onClick={() => setRequestDraftOpen((value) => !value)}
                      disabled={requestSubmitting}
                    >
                      {requestDraftOpen ? "Hide Request Form" : "Add New Request"}
                    </button>
                  </div>
                  {requestDraftOpen ? (
                    <section className="launcher-request-composer">
                      <label className="launcher-request-composer-label" htmlFor="launcher-request-draft">
                        What should be added or improved?
                      </label>
                      <textarea
                        id="launcher-request-draft"
                        className="launcher-request-textarea"
                        value={requestDraft}
                        onChange={(event) => setRequestDraft(event.target.value)}
                        placeholder="Type a request for the board..."
                        maxLength={1000}
                      />
                      <div className="launcher-request-composer-actions">
                        <button
                          type="button"
                          className="launcher-primary-btn"
                          onClick={() => void handleAddRequest()}
                          disabled={requestSubmitting}
                        >
                          {requestSubmitting ? "Saving Request..." : "Save Request"}
                        </button>
                        <button
                          type="button"
                          className="launcher-secondary-btn"
                          onClick={() => {
                            setRequestDraft("");
                            setRequestDraftOpen(false);
                          }}
                          disabled={requestSubmitting}
                        >
                          Cancel
                        </button>
                      </div>
                    </section>
                  ) : null}
                  <div className="launcher-request-list">
                    {requestsLoading ? (
                      <section className="launcher-request-entry">
                        <div className="launcher-request-empty">Loading saved requests...</div>
                      </section>
                    ) : null}
                    {!requestsLoading && requests.length === 0 ? (
                      <section className="launcher-request-entry">
                        <div className="launcher-request-empty">No requests yet. Add the first one to start the board.</div>
                      </section>
                    ) : null}
                    {!requestsLoading ? requests.map((requestEntry) => {
                      const isMutating = requestMutatingId === requestEntry.id;
                      return (
                        <section
                          key={requestEntry.id}
                          className={`launcher-request-entry ${requestEntry.done ? "is-done" : ""}`}
                        >
                          <div className="launcher-request-entry-head">
                            <label className="launcher-request-check">
                              <input
                                type="checkbox"
                                checked={requestEntry.done}
                                onChange={() => void handleToggleRequest(requestEntry)}
                                disabled={isMutating}
                              />
                              <span>{requestEntry.done ? "Complete" : "Open"}</span>
                            </label>
                            <button
                              type="button"
                              className="launcher-request-delete-btn"
                              onClick={() => void handleDeleteRequest(requestEntry)}
                              disabled={isMutating}
                              aria-label="Delete request"
                            >
                              X
                            </button>
                          </div>
                          <div className="launcher-request-entry-text">{requestEntry.text}</div>
                          <div className="launcher-request-entry-meta">{formatRequestTimestamp(requestEntry.createdAt)}</div>
                        </section>
                      );
                    }) : null}
                  </div>
                  <div className="changelog-splash-footer">
                    <div className="changelog-splash-footnote">
                      Requests are saved and shared from this launcher. Use the checkbox to mark progress and the X to remove an entry.
                    </div>
                  </div>
                </div>
              )}
              {requestsError ? (
                <p className="launcher-request-error">{requestsError}</p>
              ) : null}
            </div>
          </div>
        </section>
      </div>
    </main>
  );
}

export default WorldshaperLauncher;
