// src/client/entities/permissions.ts
// Permissions entity - Pinia store for managing user permissions

import { defineStore } from 'pinia';
import { z } from 'zod';
import { api, type RequestConfig } from '../shared/api/api.client';
import { socketService } from '../shared/api/SocketService';
import { USER_ROLES } from '@shared/constants/roles';
import { AppPermission } from '@shared/contracts/permissions';
import { useUserStore } from './user';

export { AppPermission };

/**
 * Type for user role
 */
export type UserRole = typeof USER_ROLES[keyof typeof USER_ROLES];

// Zod schema for current user permissions response
const PermissionsResponseSchema = z.array(z.string());

// Cache duration in milliseconds (5 minutes)
const CACHE_DURATION = 5 * 60 * 1000;

/**
 * Permissions store
 * Manages user permissions based on role
 */
export const usePermissionsStore = defineStore('permissions', {
  state: () => ({
    permissions: [] as AppPermission[],
    isLoading: false as boolean,
    error: null as string | null,
    lastFetchTime: null as number | null,
    currentRole: null as UserRole | null,
    socketBound: false as boolean,
    socketUnsubscribe: null as null | (() => void),
  }),

  getters: {
    /**
     * Check if the user has a specific permission
     */
    hasPermission: (state) => {
      return (permission: AppPermission | string) => {
        const userStore = useUserStore();
        const role = userStore.user?.role;

        if (!role) {
          return false;
        }

        return state.permissions.includes(permission as AppPermission);
      };
    },

    /**
     * Check if the user has any of the specified permissions
     */
    hasAnyPermission: (state) => (permissions: AppPermission[]): boolean => {
      return permissions.some((perm) => state.permissions.includes(perm));
    },

    /**
     * Check if the user has all of the specified permissions
     */
    hasAllPermissions: (state) => (permissions: AppPermission[]): boolean => {
      return permissions.every((perm) => state.permissions.includes(perm));
    },

    /**
     * Check if the user has permission for a specific resource and action
     */
    can: (state) => (resource: string, action: string): boolean => {
      const permissionString = `${resource}:${action}` as AppPermission;
      return state.permissions.includes(permissionString);
    },

    /**
     * Check if permissions have been loaded and are still fresh
     */
    isLoaded: (state) => {
      const userStore = useUserStore();
      const currentUserRole = userStore.user?.role ?? null;

      if (state.lastFetchTime === null) {
        return false;
      }
      if (!currentUserRole || state.currentRole !== currentUserRole) {
        return false;
      }
      const isStale = Date.now() - state.lastFetchTime > CACHE_DURATION;
      return !isStale;
    },

    hasFetched: (state) => {
      return state.lastFetchTime !== null;
    },
  },

  actions: {
    bindSocketEvents(): void {
      if (this.socketBound) return;

      this.socketUnsubscribe = socketService.subscribe<{ roles?: string[] }>('system:permissions_updated', async () => {
        this.lastFetchTime = null;
        await this.fetchMyPermissions();
      });

      this.socketBound = true;
    },

    unbindSocketEvents(): void {
      if (!this.socketBound) return;

      this.socketUnsubscribe?.();
      this.socketUnsubscribe = null;
      this.socketBound = false;
    },

    /**
     * Fetch permissions for the current user's role
     */
    async fetchMyPermissions(): Promise<void> {
      try {
        this.isLoading = true;
        this.error = null;

        const userStore = useUserStore();
        const role = userStore.user?.role as UserRole | undefined;

        if (!role) {
          this.permissions = [];
          this.currentRole = null;
          this.lastFetchTime = null;
          this.unbindSocketEvents();
          return;
        }

        this.bindSocketEvents();

        const response = await api.get<string[]>('/system/permissions/my', {
          schema: PermissionsResponseSchema,
        } as RequestConfig<string[]>);

        this.permissions = response.map((permission) => permission as AppPermission);
        this.currentRole = role;
        this.lastFetchTime = Date.now();
      } catch (error: any) {
        this.error = error.message || 'Failed to fetch permissions';
        console.error('[Permissions] Failed to fetch permissions:', error);
        // Fall back to empty permissions on error
        this.permissions = [];
        throw error;
      } finally {
        this.isLoading = false;
      }
    },

    /**
     * Refresh permissions from backend
     */
    async refreshPermissions(_role: UserRole): Promise<void> {
      await this.fetchMyPermissions();
    },

    /**
     * Backward-compatible alias
     */
    async fetchPermissions(_role: UserRole): Promise<void> {
      await this.fetchMyPermissions();
    },

    /**
     * Clear permissions state
     */
    clearPermissions(): void {
      this.unbindSocketEvents();
      this.permissions = [];
      this.error = null;
      this.lastFetchTime = null;
      this.currentRole = null;
    },

    /**
     * Set permissions manually (for testing or special cases)
     */
    setPermissions(permissions: AppPermission[], role: UserRole): void {
      this.permissions = permissions;
      this.currentRole = role;
      this.lastFetchTime = Date.now();
    },
  },

  persist: {
    key: 'permissions',
    storage: localStorage,
    pick: ['permissions', 'currentRole', 'lastFetchTime'],
  },
});
