"use client";

import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { api, clearTokens, getAccessToken, setTokens } from "./api";
import type { Role, Workspace } from "./types";

const WS_KEY = "legate.ws";

interface AuthState {
  booting: boolean;
  isAuthed: boolean;
  workspaces: Workspace[];
  currentWorkspace: Workspace | null;
  role: Role | null;
  login: (email: string, password: string) => Promise<void>;
  register: (email: string, password: string, name: string) => Promise<void>;
  logout: () => void;
  switchWorkspace: (id: string) => Promise<void>;
  createWorkspace: (name: string) => Promise<Workspace>;
  reloadWorkspaces: () => Promise<void>;
}

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

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [booting, setBooting] = useState(true);
  const [isAuthed, setIsAuthed] = useState(false);
  const [workspaces, setWorkspaces] = useState<Workspace[]>([]);
  const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(null);

  const pickWorkspace = useCallback(
    async (list: Workspace[]) => {
      if (list.length === 0) {
        setCurrentWorkspace(null);
        return;
      }
      const stored = typeof window !== "undefined" ? window.localStorage.getItem(WS_KEY) : null;
      const target = list.find((w) => w.id === stored) ?? list[0];
      const res = await api.switchWorkspace(target.id);
      setTokens(res.access_token);
      window.localStorage.setItem(WS_KEY, target.id);
      setCurrentWorkspace(target);
    },
    []
  );

  const boot = useCallback(async () => {
    if (!getAccessToken()) {
      setBooting(false);
      return;
    }
    try {
      const list = await api.listWorkspaces();
      setWorkspaces(list);
      await pickWorkspace(list);
      setIsAuthed(true);
    } catch {
      clearTokens();
      setIsAuthed(false);
    } finally {
      setBooting(false);
    }
  }, [pickWorkspace]);

  useEffect(() => {
    void boot();
  }, [boot]);

  const login = useCallback(
    async (email: string, password: string) => {
      const tokens = await api.login(email, password);
      setTokens(tokens.access_token, tokens.refresh_token);
      const list = await api.listWorkspaces();
      setWorkspaces(list);
      await pickWorkspace(list);
      setIsAuthed(true);
    },
    [pickWorkspace]
  );

  const register = useCallback(
    async (email: string, password: string, name: string) => {
      await api.register(email, password, name);
      await login(email, password);
    },
    [login]
  );

  const logout = useCallback(() => {
    clearTokens();
    if (typeof window !== "undefined") window.localStorage.removeItem(WS_KEY);
    setIsAuthed(false);
    setWorkspaces([]);
    setCurrentWorkspace(null);
  }, []);

  const switchWorkspace = useCallback(
    async (id: string) => {
      const res = await api.switchWorkspace(id);
      setTokens(res.access_token);
      window.localStorage.setItem(WS_KEY, id);
      setCurrentWorkspace(workspaces.find((w) => w.id === id) ?? null);
    },
    [workspaces]
  );

  const createWorkspace = useCallback(async (name: string) => {
    const res = await api.createWorkspace(name);
    setTokens(res.access_token);
    window.localStorage.setItem(WS_KEY, res.workspace.id);
    const list = await api.listWorkspaces();
    setWorkspaces(list);
    setCurrentWorkspace(list.find((w) => w.id === res.workspace.id) ?? res.workspace);
    setIsAuthed(true);
    return res.workspace;
  }, []);

  const reloadWorkspaces = useCallback(async () => {
    setWorkspaces(await api.listWorkspaces());
  }, []);

  return (
    <AuthContext.Provider
      value={{
        booting,
        isAuthed,
        workspaces,
        currentWorkspace,
        role: currentWorkspace?.role ?? null,
        login,
        register,
        logout,
        switchWorkspace,
        createWorkspace,
        reloadWorkspaces,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

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

const ROLE_PERMS: Record<Role, Set<string>> = {
  owner: new Set(["workspace", "members", "build", "connectors", "run", "approve", "view"]),
  admin: new Set(["members", "build", "connectors", "run", "approve", "view"]),
  builder: new Set(["build", "connectors", "run", "view"]),
  operator: new Set(["run", "approve", "view"]),
  viewer: new Set(["view"]),
};

export function can(role: Role | null, perm: string): boolean {
  if (!role) return false;
  return ROLE_PERMS[role].has(perm);
}
