'use client';

import { create } from 'zustand';
import type { AuthUser } from '@alqove/types';
import type { Membership } from '@alqove/api-client';
import { api } from '@/lib/api';

interface AuthState {
  user: AuthUser | null;
  token: string | null;
  /**
   * The user's active store memberships with resolved capabilities — the
   * ONE source the UI gates store features from (Port 00 §7). `null` means
   * not loaded (or the fetch failed); `[]` means loaded with none.
   */
  memberships: Membership[] | null;
  selectedStoreId: string | null;
  /** Monotonic command identity; never persisted or reset on logout. */
  commandGeneration: number;
  selectStore: (storeId: string) => void;
  isLoading: boolean;

  setAuth: (user: AuthUser, token: string) => void;
  clearAuth: () => void;
  loadFromStorage: () => Promise<void>;
  refreshMemberships: () => Promise<void>;
  login: (email: string, password: string) => Promise<void>;
  register: (name: string, email: string, password: string, phone?: string) => Promise<void>;
  logout: () => Promise<void>;
}

async function fetchMemberships(): Promise<Membership[] | null> {
  try {
    const response = await api.team.myMemberships();
    return response.data ?? [];
  } catch {
    // Auth must never depend on memberships loading; fail to "unknown".
    return null;
  }
}

const selectionKey = (userId: string) => `selected_store:${userId}`;
// Invalidates in-flight work even if a later session uses the same token/user.
let authRevision = 0;
let membershipRevision = 0;

/** Capture once at async command origin. Unlike commandGeneration, this epoch
 * changes only at auth lifecycle boundaries, not membership discovery/selection.
 * It is never persisted or reset, including same-credential session reentry.
 */
export function captureAuthEpoch(): number {
  return authRevision;
}

function reconcileSelection(userId: string, memberships: Membership[] | null): string | null {
  const preference = localStorage.getItem(selectionKey(userId));
  const selected = memberships?.find((m) => m.store_id === preference)?.store_id
    ?? memberships?.[0]?.store_id ?? null;
  if (selected) localStorage.setItem(selectionKey(userId), selected);
  else localStorage.removeItem(selectionKey(userId));
  return selected;
}

export const useAuthStore = create<AuthState>((set, get) => ({
  user: null,
  token: null,
  memberships: null,
  isLoading: true,
  selectedStoreId: null,
  commandGeneration: 0,

  selectStore: (storeId) => {
    const { user, memberships } = get();
    if (user && memberships?.some((m) => m.store_id === storeId)) {
      localStorage.setItem(selectionKey(user.id), storeId);
      set({ selectedStoreId: storeId, commandGeneration: get().commandGeneration + (get().selectedStoreId !== storeId ? 1 : 0) });
    }
  },

  setAuth: (user, token) => {
    authRevision++;
    localStorage.setItem('auth_token', token);
    set({ user, token, memberships: null, selectedStoreId: null, isLoading: false, commandGeneration: get().commandGeneration + 1 });
  },

  clearAuth: () => {
    authRevision++;
    const user = get().user;
    if (user) localStorage.removeItem(selectionKey(user.id));
    localStorage.removeItem('auth_token');
    set({ user: null, token: null, memberships: null, selectedStoreId: null, isLoading: false, commandGeneration: get().commandGeneration + 1 });
  },

  loadFromStorage: async () => {
    const revision = ++authRevision;
    const token = localStorage.getItem('auth_token');
    if (!token) {
      get().clearAuth();
      return;
    }

    set({ memberships: null, selectedStoreId: null, isLoading: true, commandGeneration: get().commandGeneration + 1 });
    try {
      const [response, memberships] = await Promise.all([api.auth.me(), fetchMemberships()]);
      if (revision !== authRevision) return;
      set({ user: response.data, token, memberships, selectedStoreId: reconcileSelection(response.data.id, memberships), isLoading: false });
    } catch {
      if (revision === authRevision) get().clearAuth();
    }
  },

  refreshMemberships: async () => {
    const revision = authRevision;
    const request = ++membershipRevision;
    const { user, token } = get();
    if (!user || !token) return;
    const memberships = await fetchMemberships();
    if (revision !== authRevision || request !== membershipRevision || get().user !== user || get().token !== token) return;
    const selectedStoreId = reconcileSelection(user.id, memberships);
    set({ memberships, selectedStoreId, commandGeneration: get().commandGeneration + (get().selectedStoreId !== selectedStoreId ? 1 : 0) });
  },

  login: async (email, password) => {
    const revision = ++authRevision;
    const response = await api.auth.login({ email, password });
    if (revision !== authRevision) return;
    const { user, token } = response.data;
    get().setAuth(user, token);
    await get().refreshMemberships();
  },

  register: async (name, email, password, phone) => {
    const revision = ++authRevision;
    const response = await api.auth.register({
      name,
      email,
      password,
      password_confirmation: password,
      ...(phone !== undefined ? { phone } : {}),
    });
    if (revision !== authRevision) return;
    const { user, token } = response.data;
    get().setAuth(user, token);
    await get().refreshMemberships();
  },

  logout: async () => {
    // Start while the outgoing token is available; clear local access immediately.
    const request = api.auth.logout();
    get().clearAuth();
    try { await request; } catch { /* Token may already be invalid. */ }
  },
}));

/** Selector: the caller's membership at a store, or undefined. */
export function selectMembershipFor(storeId: string | null | undefined) {
  return (state: AuthState): Membership | undefined =>
    storeId ? state.memberships?.find((m) => m.store_id === storeId) : undefined;
}
