import { create } from 'zustand';
import { AuthApi } from '../infrastructure/authApi';
import { connectSocket, disconnectSocket } from '../../../core/infrastructure/socket';
import type { User } from '../../../core/domain/types';

interface AuthState {
  token: string | null;
  refreshToken: string | null;
  user: User | null;
  isLoading: boolean;
  error: string | null;
  login: (username: string, password: string) => Promise<void>;
  register: (username: string, displayName: string, password: string, bio?: string) => Promise<void>;
  logout: () => void;
  checkAuth: () => Promise<void>;
  updateUser: (data: Partial<User>) => void;
  config: any;
  fetchConfig: () => Promise<void>;
}

export const useAuthStore = create<AuthState>((set, get) => ({
  token: (() => {
    const t = localStorage.getItem('knot_token');
    if (t) AuthApi.setToken(t);
    return t;
  })(),
  refreshToken: (() => {
    return localStorage.getItem('knot_refresh_token');
  })(),
  user: null,
  isLoading: true,
  error: null,
  config: null,

  fetchConfig: async () => {
    if (!get().token) return;
    try {
      const res = await AuthApi.getConfig();
      set({ config: res });
    } catch { }
  },

  login: async (username, password) => {
    try {
      set({ error: null, isLoading: true });
      const { token, refreshToken, user } = await AuthApi.login(username, password);
      localStorage.setItem('knot_token', token);
      if (refreshToken) {
        localStorage.setItem('knot_refresh_token', refreshToken);
      }
      AuthApi.setToken(token);
      connectSocket(token);
      set({ token, refreshToken, user, isLoading: false });
      await get().fetchConfig();
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : String(err);
      set({ error: msg, isLoading: false });
      throw err;
    }
  },

  register: async (username, displayName, password, bio) => {
    try {
      set({ error: null, isLoading: true });
      const { token, refreshToken, user } = await AuthApi.register(username, displayName, password, bio);
      localStorage.setItem('knot_token', token);
      if (refreshToken) {
        localStorage.setItem('knot_refresh_token', refreshToken);
      }
      AuthApi.setToken(token);
      connectSocket(token);
      set({ token, refreshToken, user, isLoading: false });
      await get().fetchConfig();
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : String(err);
      set({ error: msg, isLoading: false });
      throw err;
    }
  },

  logout: () => {
    localStorage.removeItem('knot_token');
    localStorage.removeItem('knot_refresh_token');
    AuthApi.setToken(null);
    disconnectSocket();
    set({ token: null, refreshToken: null, user: null });
  },

  checkAuth: async () => {
    const token = get().token;
    if (!token) {
      set({ isLoading: false });
      return;
    }

    // Retry up to 3 times in case server is still starting
    let lastError: unknown;
    for (let attempt = 0; attempt < 3; attempt++) {
      try {
        AuthApi.setToken(token);
        const { user, token: newToken } = await AuthApi.getMe();
        if (newToken && newToken.length > 0) {
          localStorage.setItem('knot_token', newToken);
          AuthApi.setToken(newToken);
          set({ token: newToken });
        }
        connectSocket(token);
        set({ user, isLoading: false });
        await get().fetchConfig();
        return;
      } catch (err) {
        lastError = err;
        // Only retry on network/server errors, not on auth errors (401/403)
        const msg = err instanceof Error ? err.message : '';
        if (msg.includes('Требуется авторизация') || msg.includes('Недействительный токен')) {
          break;
        }
        if (attempt < 2) {
          await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
        }
      }
    }
    console.warn('checkAuth failed:', lastError);
    // Explicitly check for 401 Unauthorized or 403 Forbidden
    const status = (lastError as any)?.status;
    const errorMsg = lastError instanceof Error ? lastError.message : String(lastError);

    if (
      status === 401 ||
      status === 403 ||
      errorMsg.includes('auth') ||
      errorMsg.includes('Недействительный токен') ||
      errorMsg.includes('Требуется авторизация')
    ) {
      localStorage.removeItem('knot_token');
      localStorage.removeItem('knot_refresh_token');
      set({ token: null, refreshToken: null, user: null, isLoading: false });
    } else {
      // Keep the token but stop loading if we're just offline/network error/500
      set({ isLoading: false });
    }
  },

  updateUser: (data) => {
    const { user } = get();
    if (user) {
      set({ user: { ...user, ...data } });
    }
  },
}));
