import { create } from 'zustand'; import { persist } from 'zustand/middleware'; export interface ContextItem { type: 'doctor' | 'clinic'; db_uuid: string; name: string; role: 'admin' | 'clinic' | 'doctor' | 'secretary' | 'staff' | 'representation' | 'user'; scope?: string | null; doctor_uuid?: string; permissions?: Record; } interface AuthState { token: string | null; refreshToken: string | null; isAuthenticated: boolean; userUuid: string | null; userName: string | null; primaryRole: 'admin' | 'clinic' | 'doctor' | 'secretary' | 'staff' | 'representation' | 'user' | null; dbUuid: string | null; dbKey: string | null; doctorUuid: string | null; context: ContextItem | null; availableContexts: ContextItem[]; login: (token: string, refreshToken?: string | null) => void; logout: () => void; refresh: () => Promise; fetchMe: () => Promise; switchContext: (dbUuid: string) => Promise; } function getToken(): string | null { try { const raw = localStorage.getItem('clinicpro-auth'); if (!raw) return null; return JSON.parse(raw)?.state?.token ?? null; } catch { return null; } } async function apiFetch(path: string, options: RequestInit = {}) { const token = getToken(); const res = await fetch(path, { ...options, headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), ...options.headers, }, }); return res.json(); } export const useAuthStore = create()( persist( (set, get) => ({ token: null, refreshToken: null, isAuthenticated: false, userUuid: null, userName: null, primaryRole: null, dbUuid: null, dbKey: null, doctorUuid: null, context: null, availableContexts: [], login: (token, refreshToken = null) => { set({ token, refreshToken, isAuthenticated: true }); get().fetchMe(); }, logout: () => { const rt = get().refreshToken; if (rt) { apiFetch('/oauth/logout', { method: 'POST', body: JSON.stringify({ refresh_token: rt }), }).catch(() => {}); } set({ token: null, refreshToken: null, isAuthenticated: false, userUuid: null, userName: null, primaryRole: null, dbUuid: null, dbKey: null, doctorUuid: null, context: null, availableContexts: [], }); }, refresh: async () => { const rt = get().refreshToken; if (!rt) return null; try { const res = await fetch('/oauth/token/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token: rt }), }); const json = await res.json(); if (!res.ok || !json?.access_token) return null; set({ token: json.access_token, refreshToken: json.refresh_token ?? rt, isAuthenticated: true, }); return json.access_token as string; } catch { return null; } }, fetchMe: async () => { try { const res = await apiFetch('/oauth/userinfo'); if (!res.success) return; const d = res.data; set({ userUuid: d.uuid, userName: d.realName, primaryRole: d.primary_role ?? null, dbUuid: d.db_uuid ?? null, dbKey: d.db_key ?? null, doctorUuid: d.doctor_uuid ?? null, context: d.context ?? null, availableContexts: d.available_contexts ?? [], }); } catch { // شبکه در دسترس نیست — state دست‌نخورده بماند } }, switchContext: async (dbUuid: string) => { const res = await apiFetch('/api/v1/auth/switch-context', { method: 'POST', body: JSON.stringify({ db_uuid: dbUuid }), }); if (res.success) { set({ dbUuid: res.data.db_uuid, dbKey: res.data.db_key, doctorUuid: res.data.context?.doctor_uuid ?? get().doctorUuid, context: res.data.context, primaryRole: res.data.context?.role ?? null, }); } }, }), { name: 'clinicpro-auth', partialize: (s) => ({ token: s.token, refreshToken: s.refreshToken, isAuthenticated: s.isAuthenticated, userUuid: s.userUuid, userName: s.userName, primaryRole: s.primaryRole, dbUuid: s.dbUuid, dbKey: s.dbKey, doctorUuid: s.doctorUuid, context: s.context, availableContexts: s.availableContexts, }), } ) );