import { redirect, useSearchParams } from "react-router";
import { useState } from "react";
import { Bell, Key, Lock, Shield, User } from "lucide-react";
import { auth } from "~/lib/auth.server";
import { findUserById, findUserByUsername, isUserInActiveDraft, updateUser, anonymizeUserAccount, USERNAME_RE } from "~/models/user";
import { findLinkedAccountsByUserId } from "~/models/account";
import { findTeamsByOwnerId, removeTeamOwner } from "~/models/team";
import { removeAllCommissionersByUserId } from "~/models/commissioner";
import { parseFlagConfig } from "~/lib/flag-types";
import { deleteCloudinaryImageByUrl } from "~/lib/cloudinary.server";
import { sendEmail, wrapInEmailTemplate, emailParagraph, escapeHtml } from "~/lib/email.server";
import { logger } from "~/lib/logger";
import { TIMEZONE_OPTIONS } from "~/components/league/TimezoneSelect";
import {
  SettingsDesktopNav,
  SettingsMobileGridNav,
  SettingsMobileSectionPill,
  type SettingsGridSection,
} from "~/components/league/settings/SettingsNavigation";
import { ProfileSection } from "~/components/user/settings/ProfileSection";
import { AccountSection } from "~/components/user/settings/AccountSection";
import { ApiSection } from "~/components/user/settings/ApiSection";
import { PrivacySection } from "~/components/user/settings/PrivacySection";
import { NotificationsSection } from "~/components/user/settings/NotificationsSection";
import type { Route } from "./+types/settings";

type SectionId = "profile" | "account" | "notifications" | "api" | "privacy";

const SECTIONS: readonly SettingsGridSection[] = [
  { id: "profile", label: "Profile", icon: User, subtitle: "Name, avatar, timezone" },
  { id: "account", label: "Account", icon: Shield, subtitle: "Email, sign-in methods" },
  { id: "notifications", label: "Notifications", icon: Bell, subtitle: "Email & Discord" },
  { id: "api", label: "API Access", icon: Key, subtitle: "Coming soon" },
  { id: "privacy", label: "Data & Privacy", icon: Lock, subtitle: "Export, delete account", isDanger: true },
] as const;

const VALID_TIMEZONES = new Set(TIMEZONE_OPTIONS.flatMap((g) => g.zones.map((z) => z.value)));
const VALID_SECTION_IDS = new Set(SECTIONS.map((s) => s.id as SectionId));

const DATA_REQUEST_COOLDOWN_MS = 30 * 24 * 60 * 60 * 1000; // 30 days

type ActionData =
  | { intent: "update-avatar-flag"; success: true }
  | { intent: "update-avatar-flag"; error: string }
  | { intent: "remove-avatar-photo"; success: true }
  | { intent: "update-profile"; success: true }
  | { intent: "update-profile"; error: string }
  | { intent: "update-discord-ping"; success: true }
  | { intent: "update-discord-ping"; error: string }
  | { intent: "update-email-draft-notifications"; success: true }
  | { intent: "update-email-draft-notifications"; error: string }
  | { intent: "request-data"; dataRequestSuccess: true }
  | { intent: "request-data"; dataRequestError: string }
  | { intent: string; error: string };

export function meta(): Route.MetaDescriptors {
  return [{ title: "Settings - Brackt" }];
}

export async function loader(args: Route.LoaderArgs) {
  const session = await auth.api.getSession({ headers: args.request.headers });
  if (!session) {
    return redirect("/login?redirectTo=/settings");
  }
  const user = await findUserById(session.user.id);
  if (!user) {
    return redirect("/");
  }
  const [isInActiveDraft, linkedAccounts] = await Promise.all([
    isUserInActiveDraft(session.user.id),
    findLinkedAccountsByUserId(session.user.id),
  ]);

  const dataRequestCooldownUntil = user.lastDataRequestAt
    ? new Date(user.lastDataRequestAt.getTime() + DATA_REQUEST_COOLDOWN_MS)
    : null;

  return { user, isInActiveDraft, linkedAccounts, dataRequestCooldownUntil };
}

export async function action(args: Route.ActionArgs): Promise<ActionData | Response> {
  const session = await auth.api.getSession({ headers: args.request.headers });
  if (!session) {
    return redirect("/login?redirectTo=/settings");
  }

  const user = await findUserById(session.user.id);
  if (!user) {
    return redirect("/");
  }

  const formData = await args.request.formData();
  const intent = formData.get("intent");

  if (intent === "update-avatar-flag") {
    const rawConfig = formData.get("flagConfig");
    if (typeof rawConfig !== "string") {
      return { intent: "update-avatar-flag", error: "Flag config is required." };
    }
    let parsedConfig: unknown;
    try {
      parsedConfig = JSON.parse(rawConfig);
    } catch {
      return { intent: "update-avatar-flag", error: "Invalid flag config." };
    }
    const flagConfig = parseFlagConfig(parsedConfig);
    if (!flagConfig) {
      return { intent: "update-avatar-flag", error: "Invalid flag config." };
    }
    await updateUser(session.user.id, { flagConfig, avatarType: "flag" });
    return { intent: "update-avatar-flag", success: true };
  }

  if (intent === "remove-avatar-photo") {
    await updateUser(session.user.id, { customAvatarUrl: null, avatarType: "flag" });
    if (user.customAvatarUrl) {
      deleteCloudinaryImageByUrl(user.customAvatarUrl).catch((err) => {
        logger.error("Failed to delete user avatar from Cloudinary:", err);
      });
    }
    return { intent: "remove-avatar-photo", success: true };
  }

  if (intent === "update-profile") {
    const username = (formData.get("username") as string | null)?.trim() ?? "";
    const timezone = formData.get("timezone") as string | null;
    const timezoneValue = timezone && VALID_TIMEZONES.has(timezone) ? timezone : undefined;

    if (username !== "") {
      if (!USERNAME_RE.test(username)) {
        return { intent: "update-profile", error: "Username must be 3–30 characters and contain only letters, numbers, underscores, or hyphens." };
      }
      const existing = await findUserByUsername(username);
      if (existing && existing.id !== session.user.id) {
        return { intent: "update-profile", error: "That username is already taken." };
      }
    }

    try {
      await updateUser(session.user.id, {
        ...(username !== "" ? { username } : {}),
        timezone: timezoneValue,
      });
    } catch {
      return { intent: "update-profile", error: "That username is already taken." };
    }
    return { intent: "update-profile", success: true };
  }

  if (intent === "update-discord-ping") {
    const enabled = formData.get("discordPingEnabled") === "true";
    if (enabled) {
      const accounts = await findLinkedAccountsByUserId(session.user.id);
      if (!accounts.some((a) => a.providerId === "discord")) {
        return { intent: "update-discord-ping", error: "Link your Discord account first." };
      }
    }
    await updateUser(session.user.id, { discordPingEnabled: enabled });
    return { intent: "update-discord-ping", success: true };
  }

  if (intent === "update-email-draft-notifications") {
    const enabled = formData.get("draftEmailNotificationsEnabled") === "true";
    await updateUser(session.user.id, { draftEmailNotificationsEnabled: enabled });
    return { intent: "update-email-draft-notifications", success: true };
  }

  if (intent === "request-data") {
    if (user.lastDataRequestAt) {
      const cooldownExpires = new Date(user.lastDataRequestAt.getTime() + DATA_REQUEST_COOLDOWN_MS);
      if (cooldownExpires > new Date()) {
        return {
          intent: "request-data",
          dataRequestError: `You already submitted a data request. You may submit another after ${cooldownExpires.toLocaleDateString()}.`,
        };
      }
    }

    const notes = (formData.get("dataRequestNotes") as string | null) ?? "";
    const html = wrapInEmailTemplate(`
      ${emailParagraph(`A data export request has been submitted by <strong>${escapeHtml(user.email)}</strong> (user ID: ${escapeHtml(user.id)}).`)}
      ${notes ? emailParagraph(`Notes: ${escapeHtml(notes)}`) : ""}
      ${emailParagraph("Please fulfill this request within 30 days per GDPR/CCPA requirements.")}
    `);
    const { error } = await sendEmail({
      to: "privacy@brackt.com",
      subject: `Data Request from ${user.email}`,
      html,
      replyTo: user.email,
    });
    if (error) {
      logger.error("Failed to send data request email:", error);
      return { intent: "request-data", dataRequestError: "Failed to submit request. Please email privacy@brackt.com directly." };
    }
    await updateUser(session.user.id, { lastDataRequestAt: new Date() });
    return { intent: "request-data", dataRequestSuccess: true };
  }

  if (intent === "delete-account") {
    const customAvatarUrl = user.customAvatarUrl;
    const ownedTeams = await findTeamsByOwnerId(session.user.id);
    await Promise.all(ownedTeams.map((team) => removeTeamOwner(team.id)));
    await removeAllCommissionersByUserId(session.user.id);
    await anonymizeUserAccount(session.user.id);
    if (customAvatarUrl) {
      deleteCloudinaryImageByUrl(customAvatarUrl).catch((err) => {
        logger.error("Failed to delete user avatar from Cloudinary after account deletion:", err);
      });
    }
    return redirect("/");
  }

  return { intent: intent as string, error: "Unknown action." };
}

export default function SettingsPage({ loaderData, actionData }: Route.ComponentProps) {
  const { user, isInActiveDraft, linkedAccounts, dataRequestCooldownUntil } = loaderData;
  const [searchParams] = useSearchParams();
  const raw = searchParams.get("section");
  const initialSection: SectionId = raw && (VALID_SECTION_IDS as Set<string>).has(raw) ? (raw as SectionId) : "profile";
  const [activeSection, setActiveSection] = useState<SectionId>(initialSection);
  const [mobileView, setMobileView] = useState<"grid" | "section">("grid");

  const handleSectionChange = (id: string, mobile = false) => {
    setActiveSection(id as SectionId);
    if (mobile) setMobileView("section");
  };

  const ad = actionData as ActionData | undefined;
  const profileSuccess = ad?.intent === "update-profile" && "success" in ad;
  const profileError = ad?.intent === "update-profile" && "error" in ad ? ad.error : undefined;
  const avatarSuccess =
    (ad?.intent === "update-avatar-flag" || ad?.intent === "remove-avatar-photo") &&
    "success" in ad;
  const dataRequestSuccess = ad?.intent === "request-data" && "dataRequestSuccess" in ad;
  const dataRequestError =
    ad?.intent === "request-data" && "dataRequestError" in ad ? ad.dataRequestError : undefined;

  return (
    <div className="container mx-auto max-w-5xl px-4 py-6 sm:py-8">
      <div className="mb-6">
        <h1 className="text-2xl font-bold">Settings</h1>
      </div>

      {mobileView === "grid" && (
        <div className="lg:hidden">
          <SettingsMobileGridNav
            sections={SECTIONS}
            onSectionChange={(id) => handleSectionChange(id, true)}
          />
        </div>
      )}

      {mobileView === "section" && (
        <SettingsMobileSectionPill onShowGrid={() => setMobileView("grid")} />
      )}

      <div className={`grid gap-6 lg:grid-cols-[220px_minmax(0,1fr)] ${mobileView === "grid" ? "hidden lg:grid" : "grid"}`}>
        <SettingsDesktopNav
          sections={SECTIONS}
          activeSection={activeSection}
          onSectionChange={(id) => handleSectionChange(id)}
        />

        <main className="min-w-0">
          {activeSection === "profile" && (
            <ProfileSection
              user={user}
              isInActiveDraft={isInActiveDraft}
              success={profileSuccess || avatarSuccess}
              error={profileError}
            />
          )}
          {activeSection === "account" && (
            <AccountSection email={user.email} linkedAccounts={linkedAccounts} />
          )}
          {activeSection === "notifications" && (
            <NotificationsSection
              discordPingEnabled={user.discordPingEnabled}
              hasDiscordLinked={linkedAccounts.some((a) => a.providerId === "discord")}
              draftEmailNotificationsEnabled={user.draftEmailNotificationsEnabled}
              onNavigateToAccount={() => handleSectionChange("account")}
            />
          )}
          {activeSection === "api" && <ApiSection />}
          {activeSection === "privacy" && (
            <PrivacySection
              userEmail={user.email}
              dataRequestCooldownUntil={dataRequestCooldownUntil}
              dataRequestSuccess={dataRequestSuccess}
              dataRequestError={dataRequestError}
            />
          )}
        </main>
      </div>
    </div>
  );
}
