"use client";

import { createContext, useContext, useEffect, useState } from "react";
import {
  type AccountRole,
  type Booking,
  type ChildProfile,
  type RooNotification,
  type SportName,
  type SportsEvent,
  getEvent,
  withLiveSeats,
} from "@/lib/demo-data";
import { getPublicChildDisplay } from "@/lib/privacy";
import {
  RegistrationError,
  assertCanRegister,
  buildChildProfile,
} from "@/lib/registration";
import { logAIRegistryEntry } from "@/lib/traceability";

export type CoachStatus = "not-started" | "pending" | "approved" | "rejected" | "expired";

export type CoachApplication = {
  wwccLastFour: string;
  expiryDate: string;
  suburb: string;
  preferredSports: SportName[];
  availability: string;
  experience: string;
  submittedAt: string;
};

type CoachApplicationInput = Omit<CoachApplication, "wwccLastFour" | "submittedAt"> & {
  wwccNumber: string;
};

export type SessionUser = {
  id: string;
  role: AccountRole;
  email: string;
  firstName: string;
  lastName: string;
  accountStatus: "pending" | "active" | "suspended" | "archived";
  mustChangePassword: boolean;
};

type DemoState = {
  ownerUserId: string;
  children: ChildProfile[];
  activeChildId: string;
  bookings: Booking[];
  eventJoined: Record<string, number>;
  notifications: RooNotification[];
  coachStatus: CoachStatus;
  coachApplication: CoachApplication | null;
  coachEventRequests: string[];
  coachAssignments: string[];
};

type AppContextValue = DemoState & {
  user: SessionUser | null;
  role: AccountRole;
  firstName: string;
  isAuthenticated: boolean;
  authLoading: boolean;
  coachApplicationLoading: boolean;
  setAuthenticatedUser: (user: SessionUser, isNewAccount?: boolean) => void;
  clearAuthenticatedUser: () => void;
  addChild: (profile: Omit<ChildProfile, "id"> | {
    nickname: string;
    dateOfBirth: string;
    primarySport?: SportName;
    medicalAlert?: string;
  }) => Promise<ChildProfile>;
  updateChild: (
    childId: string,
    profile: {
      nickname: string;
      dateOfBirth: string;
      primarySport: SportName;
      medicalAlert: string;
    },
  ) => Promise<ChildProfile>;
  setActiveChildId: (id: string) => void;
  getLiveEvent: (eventId: string) => SportsEvent | undefined;
  startRegistration: (input: {
    eventId: string;
    participantId: string;
    waiverAccepted: boolean;
  }) => Booking;
  confirmPayment: (bookingId: string) => Booking;
  participantDisplayName: (participantId: string) => string;
  submitCoachApplication: (application: CoachApplicationInput) => Promise<void>;
  requestCoachEvent: (eventId: string) => void;
  markNotificationsRead: () => void;
};

const STORAGE_KEY = "roosports-local-state-v3";

const initialState: DemoState = {
  ownerUserId: "",
  children: [],
  activeChildId: "",
  bookings: [],
  eventJoined: {},
  notifications: [],
  coachStatus: "not-started",
  coachApplication: null,
  coachEventRequests: [],
  coachAssignments: [],
};

const AppContext = createContext<AppContextValue | null>(null);

function normalizeChild(child: Partial<ChildProfile> & { id: string; nickname: string }): ChildProfile {
  return {
    id: child.id,
    nickname: child.nickname,
    dateOfBirth: child.dateOfBirth || "2016-01-01",
    ageGroup: child.ageGroup || "U10",
    primarySport: child.primarySport || "Soccer",
    medicalAlert: child.medicalAlert || "",
  };
}

function normalizeBooking(booking: Partial<Booking> & { id: string; eventId: string; participantId: string }): Booking {
  return {
    id: booking.id,
    eventId: booking.eventId,
    participantId: booking.participantId,
    bookedAt: booking.bookedAt || new Date().toISOString(),
    status: booking.status || "confirmed",
    paymentStatus: booking.paymentStatus || (booking.status === "pending" ? "pending" : "paid"),
    waiverAccepted: booking.waiverAccepted ?? true,
  };
}

async function safeJson<T>(response: Response): Promise<T | null> {
  try {
    const text = await response.text();
    if (!text || !text.trim()) return null;
    return JSON.parse(text) as T;
  } catch {
    return null;
  }
}

export function AppProvider({ children }: { children: React.ReactNode }) {
  const [state, setState] = useState<DemoState>(initialState);
  const [user, setUser] = useState<SessionUser | null>(null);
  const [authLoading, setAuthLoading] = useState(true);
  const [coachApplicationLoading, setCoachApplicationLoading] = useState(true);
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    const restoreState = window.setTimeout(() => {
      const savedState = window.localStorage.getItem(STORAGE_KEY);
      if (savedState) {
        try {
          const parsedState = JSON.parse(savedState) as Partial<DemoState>;
          setState({
            ...initialState,
            ...parsedState,
            children: (parsedState.children || []).map(normalizeChild),
            bookings: (parsedState.bookings || []).map(normalizeBooking),
            eventJoined: parsedState.eventJoined || {},
            coachApplication: parsedState.coachApplication || null,
            coachEventRequests: parsedState.coachEventRequests || [],
            coachAssignments: parsedState.coachAssignments || [],
          });
        } catch {
          window.localStorage.removeItem(STORAGE_KEY);
        }
      }
      setLoaded(true);
    }, 0);

    return () => window.clearTimeout(restoreState);
  }, []);

  useEffect(() => {
    if (!user || user.role === "admin") return;

    const controller = new AbortController();

    const loadCoachApplication = async () => {
      try {
        const response = await fetch("/api/coach/application", {
          credentials: "include",
          cache: "no-store",
          signal: controller.signal,
        });
        if (!response.ok) return;

        const payload = await safeJson<{
          status: CoachStatus;
          application: CoachApplication | null;
        }>(response);
        if (payload) {
          setState((current) => ({
            ...current,
            coachStatus: payload.status,
            coachApplication: payload.application,
          }));
        }
      } catch (error) {
        if (error instanceof DOMException && error.name === "AbortError") return;
      } finally {
        if (!controller.signal.aborted) setCoachApplicationLoading(false);
      }
    };

    const refreshWhenVisible = () => {
      if (document.visibilityState === "visible") void loadCoachApplication();
    };

    void loadCoachApplication();
    window.addEventListener("focus", refreshWhenVisible);
    document.addEventListener("visibilitychange", refreshWhenVisible);

    return () => {
      controller.abort();
      window.removeEventListener("focus", refreshWhenVisible);
      document.removeEventListener("visibilitychange", refreshWhenVisible);
    };
  }, [user]);

  useEffect(() => {
    if (!user || user.role !== "parent") return;

    const controller = new AbortController();
    fetch("/api/children", {
      credentials: "include",
      cache: "no-store",
      signal: controller.signal,
    })
      .then(async (response) => {
        if (!response.ok) return null;
        return safeJson<{ children: ChildProfile[] }>(response);
      })
      .then((payload) => {
        if (!payload?.children) return;
        setState((current) => ({
          ...current,
          children: payload.children,
          activeChildId: payload.children.some((child) => child.id === current.activeChildId)
            ? current.activeChildId
            : payload.children[0]?.id || "",
        }));
      })
      .catch((error: unknown) => {
        if (error instanceof DOMException && error.name === "AbortError") return;
      });

    return () => controller.abort();
  }, [user]);

  useEffect(() => {
    const controller = new AbortController();
    fetch("/api/auth/me", {
      credentials: "include",
      cache: "no-store",
      signal: controller.signal,
    })
      .then(async (response) => {
        if (!response.ok) return null;
        return safeJson<{ user?: SessionUser }>(response);
      })
      .then((payload) => {
        const sessionUser = payload?.user || null;
        setUser(sessionUser);
        if (sessionUser) {
          setState((current) =>
            current.ownerUserId === sessionUser.id
              ? current
              : { ...initialState, ownerUserId: sessionUser.id },
          );
        }
        setAuthLoading(false);
      })
      .catch((error: unknown) => {
        if (error instanceof DOMException && error.name === "AbortError") return;
        setUser(null);
        setAuthLoading(false);
      });

    return () => controller.abort();
  }, []);

  useEffect(() => {
    if (loaded) window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
  }, [loaded, state]);

  const setAuthenticatedUser = (nextUser: SessionUser, isNewAccount = false) => {
    setUser(nextUser);
    setAuthLoading(false);
    setCoachApplicationLoading(true);
    setState((current) =>
      !isNewAccount && current.ownerUserId === nextUser.id
        ? current
        : {
            ...initialState,
            ownerUserId: nextUser.id,
            coachStatus:
              nextUser.role === "coach" ? "not-started" : initialState.coachStatus,
          },
    );
  };

  const [dynamicEvents, setDynamicEvents] = useState<SportsEvent[]>([]);

  useEffect(() => {
    fetch("/api/events")
      .then((res) => (res.ok ? res.json() : { events: [] }))
      .then((data) => {
        if (Array.isArray(data.events)) {
          setDynamicEvents(data.events);
        }
      })
      .catch(() => {});
  }, []);

  const getLiveEvent = (eventId: string) => {
    const event = dynamicEvents.find((e) => e.id === eventId) || getEvent(eventId);
    if (!event) return undefined;
    return withLiveSeats(event, state.eventJoined[eventId]);
  };

  const addChild = async (
    profile:
      | Omit<ChildProfile, "id">
      | {
          nickname: string;
          dateOfBirth: string;
          primarySport?: SportName;
          medicalAlert?: string;
      },
  ) => {
    const validatedProfile = buildChildProfile(profile);
    let response: Response | null = null;

    try {
      response = await fetch("/api/children", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        credentials: "include",
        body: JSON.stringify(validatedProfile),
      });
    } catch {
      // A network failure can still use the local demo state.
    }

    if (response) {
      const payload = await safeJson<{ child?: ChildProfile; error?: string }>(response);
      if (response.ok && payload?.child) {
        const child = payload.child;
        setState((current) => ({
          ...current,
          children: [...current.children.filter((item) => item.id !== child.id), child],
          activeChildId: child.id,
        }));
        return child;
      }

      throw new Error(payload?.error || "Unable to create child profile.");
    }

    const localChild: ChildProfile = {
      id: `child-${Date.now()}`,
      ...validatedProfile,
    };

    setState((current) => ({
      ...current,
      children: [...current.children.filter((item) => item.id !== localChild.id), localChild],
      activeChildId: localChild.id,
    }));
    return localChild;
  };

  const updateChild = async (
    childId: string,
    profile: {
      nickname: string;
      dateOfBirth: string;
      primarySport: SportName;
      medicalAlert: string;
    },
  ) => {
    const validatedProfile = buildChildProfile(profile);
    let response: Response | null = null;

    try {
      response = await fetch(`/api/children/${childId}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        credentials: "include",
        body: JSON.stringify(validatedProfile),
      });
    } catch {
      // A network failure can still use the local demo state.
    }

    if (response) {
      const payload = await safeJson<{ child?: ChildProfile; error?: string }>(response);
      if (response.ok && payload?.child) {
        const updated = payload.child;
        setState((current) => ({
          ...current,
          children: current.children.map((child) => (child.id === childId ? updated : child)),
        }));
        return updated;
      }

      throw new Error(payload?.error || "Unable to update child profile.");
    }

    const localUpdated: ChildProfile = {
      id: childId,
      ...validatedProfile,
    };

    setState((current) => ({
      ...current,
      children: current.children.map((child) => (child.id === childId ? localUpdated : child)),
    }));
    return localUpdated;
  };

  const startRegistration = (input: {
    eventId: string;
    participantId: string;
    waiverAccepted: boolean;
  }) => {
    const event = getLiveEvent(input.eventId);
    if (!event) throw new RegistrationError("event_unavailable", "Activity not found.");

    const role = user?.role || "parent";
    assertCanRegister({
      event,
      role,
      participantId: input.participantId,
      children: state.children,
      bookings: state.bookings,
      waiverAccepted: input.waiverAccepted,
      adultAge: role === "player" ? 25 : null,
    });

    const amount = event.price;
    const booking: Booking = {
      id: `booking-${Date.now()}`,
      eventId: event.id,
      participantId: input.participantId,
      bookedAt: new Date().toISOString(),
      status: "pending",
      paymentStatus: amount === 0 ? "not_required" : "pending",
      waiverAccepted: true,
    };

    setState((current) => ({
      ...current,
      bookings: [...current.bookings.filter((item) => item.status !== "pending"), booking],
    }));

    logAIRegistryEntry("Event registration", "Created pending registration awaiting payment", {
      eventId: event.id,
      participantId: input.participantId,
      amount,
    });

    return booking;
  };

  const confirmPayment = (bookingId: string) => {
    const booking = state.bookings.find((item) => item.id === bookingId);
    if (!booking || booking.status !== "pending") {
      throw new RegistrationError("event_unavailable", "No pending registration to confirm.");
    }

    const event = getLiveEvent(booking.eventId);
    if (!event) throw new RegistrationError("event_unavailable", "Activity not found.");

    assertCanRegister({
      event,
      role: user?.role || "parent",
      participantId: booking.participantId,
      children: state.children,
      bookings: state.bookings.filter((item) => item.id !== booking.id),
      waiverAccepted: true,
      adultAge: user?.role === "player" ? 25 : null,
    });

    const confirmed: Booking = {
      ...booking,
      status: "confirmed",
      paymentStatus: event.price === 0 ? "not_required" : "paid",
    };

    setState((current) => {
      const liveJoined = current.eventJoined[booking.eventId] ?? event.joined;
      return {
        ...current,
        bookings: current.bookings.map((item) => (item.id === bookingId ? confirmed : item)),
        eventJoined: { ...current.eventJoined, [booking.eventId]: liveJoined + 1 },
        notifications: [
          {
            id: `notice-${Date.now()}`,
            title: "Registration confirmed",
            message: `${event.title} registration is confirmed.`,
            time: "Just now",
            icon: "check_circle",
            unread: true,
          },
          ...current.notifications,
        ],
      };
    });

    logAIRegistryEntry("Event registration", "Confirmed registration after payment", {
      bookingId,
      eventId: booking.eventId,
      participantId: booking.participantId,
    });

    return confirmed;
  };

  const participantDisplayName = (participantId: string) => {
    if (participantId === "self") {
      return user ? `${user.firstName} (me)` : "Me";
    }
    const child = state.children.find((item) => item.id === participantId);
    return child ? getPublicChildDisplay(child) : "Athlete";
  };

  const submitCoachApplication = async (application: CoachApplicationInput) => {
    const response = await fetch("/api/coach/application", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify(application),
    });
    const payload = await safeJson<{
      status?: CoachStatus;
      application?: CoachApplication;
      error?: string;
    }>(response);
    if (!response.ok || !payload?.status || !payload?.application) {
      throw new Error(payload?.error || "Unable to submit the coach application.");
    }
    setState((current) => ({
      ...current,
      coachStatus: payload.status!,
      coachApplication: payload.application!,
    }));
  };

  const value: AppContextValue = {
    ...state,
    user,
    role: user?.role || "parent",
    firstName: user?.firstName || "",
    isAuthenticated: user !== null,
    authLoading,
    coachApplicationLoading,
    setAuthenticatedUser,
    clearAuthenticatedUser: () => {
      setUser(null);
      setAuthLoading(false);
    },
    addChild,
    updateChild,
    setActiveChildId: (activeChildId) => setState((current) => ({ ...current, activeChildId })),
    getLiveEvent,
    startRegistration,
    confirmPayment,
    participantDisplayName,
    submitCoachApplication,
    requestCoachEvent: (eventId) =>
      setState((current) => ({
        ...current,
        coachEventRequests: current.coachEventRequests.includes(eventId)
          ? current.coachEventRequests
          : [...current.coachEventRequests, eventId],
      })),
    markNotificationsRead: () =>
      setState((current) => ({
        ...current,
        notifications: current.notifications.map((notice) => ({ ...notice, unread: false })),
      })),
  };

  return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}

export function useRooSports() {
  const context = useContext(AppContext);
  if (!context) throw new Error("useRooSports must be used inside AppProvider");
  return context;
}
