"use client";

import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from "react";

import { api, ApiError } from "./api";
import { components } from "./generated/api";

// Permission type from generated OpenAPI schema
export type PermissionType = components["schemas"]["Permission"];

// Permission constants - derived from backend's Permission enum via OpenAPI.
// The type annotation ensures compile-time validation against the generated schema.
// Adding a new permission in the backend will cause a type error here until updated.
export const Permission: Record<string, PermissionType> = {
  VIEW_COUNTER: "view_counter",
  INCREMENT_COUNTER: "increment_counter",
  USE_SUM: "use_sum",
  VIEW_AUDIT: "view_audit",
  MANAGE_OWN_PROFILE: "manage_own_profile",
  MANAGE_INVITES: "manage_invites",
  VIEW_OWN_INVITES: "view_own_invites",
  // Booking permissions (regular users)
  BOOK_APPOINTMENT: "book_appointment",
  VIEW_OWN_APPOINTMENTS: "view_own_appointments",
  CANCEL_OWN_APPOINTMENT: "cancel_own_appointment",
  // Availability/Appointments permissions (admin)
  MANAGE_AVAILABILITY: "manage_availability",
  VIEW_ALL_APPOINTMENTS: "view_all_appointments",
  CANCEL_ANY_APPOINTMENT: "cancel_any_appointment",
} as const;

// Use generated type from OpenAPI schema
type User = components["schemas"]["UserResponse"];

interface AuthContextType {
  user: User | null;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<void>;
  register: (email: string, password: string, inviteIdentifier: string) => Promise<void>;
  logout: () => Promise<void>;
  hasPermission: (permission: PermissionType) => boolean;
  hasRole: (role: string) => boolean;
}

const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    checkAuth();
  }, []);

  const checkAuth = async () => {
    try {
      const userData = await api.get<User>("/api/auth/me");
      setUser(userData);
    } catch {
      // Not authenticated
    } finally {
      setIsLoading(false);
    }
  };

  const login = async (email: string, password: string) => {
    try {
      const userData = await api.post<User>("/api/auth/login", { email, password });
      setUser(userData);
    } catch (err) {
      if (err instanceof ApiError) {
        const data = err.data as { detail?: string };
        throw new Error(data?.detail || "Login failed");
      }
      throw err;
    }
  };

  const register = async (email: string, password: string, inviteIdentifier: string) => {
    try {
      const userData = await api.post<User>("/api/auth/register", {
        email,
        password,
        invite_identifier: inviteIdentifier,
      });
      setUser(userData);
    } catch (err) {
      if (err instanceof ApiError) {
        const data = err.data as { detail?: string };
        throw new Error(data?.detail || "Registration failed");
      }
      throw err;
    }
  };

  const logout = async () => {
    try {
      await api.post("/api/auth/logout");
    } catch {
      // Ignore errors on logout
    }
    setUser(null);
  };

  const hasPermission = useCallback(
    (permission: PermissionType): boolean => {
      return user?.permissions.includes(permission) ?? false;
    },
    [user]
  );

  const hasRole = useCallback(
    (role: string): boolean => {
      return user?.roles.includes(role) ?? false;
    },
    [user]
  );

  return (
    <AuthContext.Provider
      value={{
        user,
        isLoading,
        login,
        register,
        logout,
        hasPermission,
        hasRole,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}
