import { useState, useEffect, useCallback } from 'react';
import { useApiRequest } from './useApi';
import { getUserDataCache } from '../utils/userCache';
import { usePermissions, type UserPermissions } from './usePermissions';
import {
  fetchRoles as fetchRolesApi,
  fetchRoleById as fetchRoleByIdApi,
  createRole as createRoleApi,
  updateRole as updateRoleApi,
  deleteRole as deleteRoleApi,
  type Role,
  type RoleUpdateData,
  type PaginationParams
} from '../api/roleApi';
import { fetchAttributes } from '../api/attributesApi';
import type { AttributeDefinition } from '../api/attributesApi';
import {
  isCheckboxType,
  isSelectType,
  isMultiselectType,
  isDateTimeType,
  isTextareaType,
  type AttributeType
} from '../utils/attributeTypeMapper';

// Re-export types for backward compatibility
export type { Role, RoleUpdateData, AttributeDefinition, PaginationParams };

// Helper function to detect TextMultilingual objects
// TextMultilingual has structure: { en: string, ge?: string, fr?: string, it?: string }
const isTextMultilingual = (value: any): boolean => {
  if (!value || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) {
    return false;
  }
  // Check if it has 'en' property (required) and optionally other language codes
  return 'en' in value && typeof value.en === 'string';
};

// Helper function to check if a field name suggests it's a multilingual field
// Only specific fields should be multilingual, not fields that just contain these words
const isMultilingualFieldName = (fieldName: string): boolean => {
  const lowerFieldName = fieldName.toLowerCase();
  
  // Exact matches for multilingual fields
  const exactMultilingualFields = ['description'];
  
  // Fields that end with these patterns (but not roleLabel, etc.)
  const multilingualPatterns = [
    /^description$/i,
    /^label$/i,  // Only exact "label", not "roleLabel"
    /^title$/i,  // Only exact "title"
    /^name$/i    // Only exact "name", not field names containing "name"
  ];
  
  // Check exact matches first
  if (exactMultilingualFields.includes(lowerFieldName)) {
    return true;
  }
  
  // Check patterns - but exclude fields like "roleLabel" which should be strings
  const excludedFields = ['rolelabel', 'role_label', 'rolename', 'role_name', 'username', 'user_name'];
  if (excludedFields.includes(lowerFieldName)) {
    return false;
  }
  
  // Check if it matches multilingual patterns (exact match, not contains)
  return multilingualPatterns.some(pattern => pattern.test(fieldName));
};

// RBAC roles hook (list, update, delete) - following mandates pattern
export function useRbacRoles() {
  const [roles, setRoles] = useState<Role[]>([]);
  const [attributes, setAttributes] = useState<AttributeDefinition[]>([]);
  const [permissions, setPermissions] = useState<UserPermissions | null>(null);
  const [pagination, setPagination] = useState<{
    currentPage: number;
    pageSize: number;
    totalItems: number;
    totalPages: number;
  } | null>(null);
  const { request, isLoading: loading, error } = useApiRequest<null, Role[]>();
  const { checkPermission } = usePermissions();

  // Fetch attributes from backend
  const fetchAttributesData = useCallback(async () => {
    try {
      const attrs = await fetchAttributes(request, 'Role');
      setAttributes(attrs);
      return attrs;
    } catch (error: any) {
      // Don't log 429 errors as errors (they're rate limit warnings)
      if (error.response?.status === 429) {
        console.warn('Rate limit exceeded while fetching role attributes. Please wait.');
      } else if (error.response?.status !== 401) {
        // Only log non-auth errors (401 is expected when not logged in)
        console.error('Error fetching attributes:', error);
      }
      setAttributes([]);
      return [];
    }
  }, [request]);

  // Fetch permissions from backend
  const fetchPermissions = useCallback(async () => {
    try {
      const perms = await checkPermission('DATA', 'Role');
      setPermissions(perms);
      return perms;
    } catch (error: any) {
      console.error('Error fetching permissions:', error);
      const defaultPerms: UserPermissions = {
        view: false,
        read: 'n',
        create: 'n',
        update: 'n',
        delete: 'n',
      };
      setPermissions(defaultPerms);
      return defaultPerms;
    }
  }, [checkPermission]);

  const fetchRoles = useCallback(async (params?: PaginationParams) => {
    try {
      const requestParams: any = {};
      
      // Build pagination object if provided
      if (params) {
        const paginationObj: any = {};
        
        if (params.page !== undefined) paginationObj.page = params.page;
        if (params.pageSize !== undefined) paginationObj.pageSize = params.pageSize;
        if (params.sort) paginationObj.sort = params.sort;
        if (params.filters) paginationObj.filters = params.filters;
        if (params.search) paginationObj.search = params.search;
        
        if (Object.keys(paginationObj).length > 0) {
          requestParams.pagination = JSON.stringify(paginationObj);
        }
      }
      
      const data = await fetchRolesApi(request, params);
      
      // Handle paginated response
      if (data && typeof data === 'object' && 'items' in data) {
        const items = Array.isArray(data.items) ? data.items : [];
        setRoles(items);
        if (data.pagination) {
          setPagination(data.pagination);
        }
      } else {
        // Handle non-paginated response (backward compatibility)
        const items = Array.isArray(data) ? data : [];
        setRoles(items);
        setPagination(null);
      }
    } catch (error: any) {
      // Error is already handled by useApiRequest
      setRoles([]);
      setPagination(null);
    }
  }, [request]);

  // Optimistically remove a role from the local state
  const removeOptimistically = (roleId: string) => {
    setRoles(prevRoles => prevRoles.filter(role => role.id !== roleId));
  };

  // Optimistically update a role in the local state
  const updateOptimistically = (roleId: string, updateData: Partial<Role>) => {
    setRoles(prevRoles => 
      prevRoles.map(role => 
        role.id === roleId 
          ? { ...role, ...updateData }
          : role
      )
    );
  };

  // Fetch a single role by ID
  const fetchRoleById = useCallback(async (roleId: string): Promise<Role | null> => {
    return await fetchRoleByIdApi(request, roleId);
  }, [request]);

  // Generate edit fields from attributes dynamically using attributeTypeMapper utilities
  const generateEditFieldsFromAttributes = useCallback((): Array<{
    key: string;
    label: string;
    type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'multiselect' | 'readonly';
    editable?: boolean;
    required?: boolean;
    validator?: (value: any) => string | null;
    minRows?: number;
    maxRows?: number;
    options?: Array<{ value: string | number; label: string }>;
    optionsReference?: string; // For options that need to be fetched (e.g., "user.role")
  }> => {
    if (!attributes || attributes.length === 0) {
      return [];
    }

    const editableFields = attributes
      .filter(attr => {
        // Filter out non-editable fields based on readonly/editable flags
        if (attr.readonly === true || attr.editable === false) {
          return false; // Don't show readonly fields in edit form
        }
        // Also filter out common non-editable fields
        const nonEditableFields = ['id', 'roleId', '_createdBy', '_hideDelete'];
        return !nonEditableFields.includes(attr.name);
      })
      .map(attr => {
        // Map backend attribute type to form field type using attributeTypeMapper utilities
        let fieldType: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'multiselect' | 'readonly' = 'string';
        let options: Array<{ value: string | number; label: string }> | undefined = undefined;
        let optionsReference: string | undefined = undefined;
        
        const attrType = attr.type as AttributeType;
        
        // Use attributeTypeMapper utilities to determine field type
        if (isCheckboxType(attrType)) {
          fieldType = 'boolean';
        } else if (attrType === 'email') {
          fieldType = 'email';
        } else if (isDateTimeType(attrType)) {
          fieldType = 'date';
        } else if (isSelectType(attrType)) {
          fieldType = 'enum';
          // Handle options - can be array or string reference
          const attrOptions = (attr as any).options;
          if (Array.isArray(attrOptions)) {
            options = attrOptions.map((opt: any) => {
              const labelValue = typeof opt.label === 'string' 
                ? opt.label 
                : opt.label?.en || opt.label?.[Object.keys(opt.label || {})[0]] || String(opt.value);
              return {
                value: opt.value,
                label: labelValue
              };
            });
          } else if (typeof attrOptions === 'string') {
            // Options reference (e.g., "user.role", "auth.authority")
            optionsReference = attrOptions;
          }
        } else if (isMultiselectType(attrType)) {
          fieldType = 'multiselect';
          // Handle options - can be array or string reference
          const attrOptions = (attr as any).options;
          if (Array.isArray(attrOptions)) {
            options = attrOptions.map((opt: any) => {
              const labelValue = typeof opt.label === 'string' 
                ? opt.label 
                : opt.label?.en || opt.label?.[Object.keys(opt.label || {})[0]] || String(opt.value);
              return {
                value: opt.value,
                label: labelValue
              };
            });
          } else if (typeof attrOptions === 'string') {
            // Options reference (e.g., "user.role", "auth.authority")
            optionsReference = attrOptions;
          }
        } else if (isTextareaType(attrType)) {
          fieldType = 'textarea';
        } else if (attrType === 'readonly') {
          fieldType = 'readonly';
        } else {
          fieldType = 'string';
        }

        // Define validators and required fields
        let required = attr.required === true;
        let validator: ((value: any) => string | null) | undefined = undefined;
        let minRows: number | undefined = undefined;
        let maxRows: number | undefined = undefined;

        // Email validation
        if (fieldType === 'email') {
          validator = (value: any) => {
            if (required && (!value || (typeof value === 'string' && value.trim() === ''))) {
              return 'Email cannot be empty';
            }
            if (value && typeof value === 'string' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
              return 'Invalid email format';
            }
            return null;
          };
        }
        // Textarea settings
        else if (fieldType === 'textarea') {
          minRows = 4;
          maxRows = 8;
          if (attr.name.toLowerCase().includes('content')) {
            minRows = 6;
            maxRows = 12;
          }
        }
        // Multiselect validation
        else if (fieldType === 'multiselect' && required) {
          validator = (value: any[]) => {
            if (!value || !Array.isArray(value) || value.length === 0) {
              return `${attr.label} is required`;
            }
            return null;
          };
        }

        return {
          key: attr.name,
          label: attr.label || attr.name,
          type: fieldType,
          editable: (attr as any).editable !== false && (attr as any).readonly !== true,
          required,
          validator,
          minRows,
          maxRows,
          options,
          optionsReference
        };
      });

    return editableFields;
  }, [attributes]);

  // Generate create fields from attributes dynamically using attributeTypeMapper utilities
  const generateCreateFieldsFromAttributes = useCallback((): Array<{
    key: string;
    label: string;
    type: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'multiselect' | 'readonly';
    required?: boolean;
    validator?: (value: any) => string | null;
    minRows?: number;
    maxRows?: number;
    options?: Array<{ value: string | number; label: string }>;
    optionsReference?: string;
    placeholder?: string;
  }> => {
    if (!attributes || attributes.length === 0) {
      return [];
    }

    const createFields = attributes
      .filter(attr => {
        // Filter out non-editable fields and auto-generated fields for create forms
        if (attr.readonly === true || attr.editable === false) {
          return false;
        }
        // Filter out ID fields and other auto-generated fields
        const nonEditableFields = ['id', 'roleId', '_createdBy', '_hideDelete'];
        return !nonEditableFields.includes(attr.name);
      })
      .map(attr => {
        // Map backend attribute type to form field type using attributeTypeMapper utilities
        let fieldType: 'string' | 'boolean' | 'email' | 'textarea' | 'date' | 'enum' | 'multiselect' | 'readonly' = 'string';
        let options: Array<{ value: string | number; label: string }> | undefined = undefined;
        let optionsReference: string | undefined = undefined;
        
        const attrType = attr.type as AttributeType;
        
        // Use attributeTypeMapper utilities to determine field type
        if (isCheckboxType(attrType)) {
          fieldType = 'boolean';
        } else if (attrType === 'email') {
          fieldType = 'email';
        } else if (isDateTimeType(attrType)) {
          fieldType = 'date';
        } else if (isSelectType(attrType)) {
          fieldType = 'enum';
          const attrOptions = (attr as any).options;
          if (Array.isArray(attrOptions)) {
            options = attrOptions.map((opt: any) => {
              const labelValue = typeof opt.label === 'string' 
                ? opt.label 
                : opt.label?.en || opt.label?.[Object.keys(opt.label || {})[0]] || String(opt.value);
              return {
                value: opt.value,
                label: labelValue
              };
            });
          } else if (typeof attrOptions === 'string') {
            optionsReference = attrOptions;
          }
        } else if (isMultiselectType(attrType)) {
          fieldType = 'multiselect';
          const attrOptions = (attr as any).options;
          if (Array.isArray(attrOptions)) {
            options = attrOptions.map((opt: any) => {
              const labelValue = typeof opt.label === 'string' 
                ? opt.label 
                : opt.label?.en || opt.label?.[Object.keys(opt.label || {})[0]] || String(opt.value);
              return {
                value: opt.value,
                label: labelValue
              };
            });
          } else if (typeof attrOptions === 'string') {
            optionsReference = attrOptions;
          }
        } else if (isTextareaType(attrType)) {
          fieldType = 'textarea';
        } else {
          fieldType = 'string';
        }

        // Define validators and required fields
        let required = attr.required === true;
        let validator: ((value: any) => string | null) | undefined = undefined;
        let minRows: number | undefined = undefined;
        let maxRows: number | undefined = undefined;

        // Email validation
        if (fieldType === 'email') {
          validator = (value: any) => {
            if (required && (!value || (typeof value === 'string' && value.trim() === ''))) {
              return 'Email cannot be empty';
            }
            if (value && typeof value === 'string' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
              return 'Invalid email format';
            }
            return null;
          };
        }
        // Textarea settings
        else if (fieldType === 'textarea') {
          minRows = 4;
          maxRows = 8;
          if (attr.name.toLowerCase().includes('content')) {
            minRows = 6;
            maxRows = 12;
          }
        }
        // Multiselect validation
        else if (fieldType === 'multiselect' && required) {
          validator = (value: any[]) => {
            if (!value || !Array.isArray(value) || value.length === 0) {
              return `${attr.label} is required`;
            }
            return null;
          };
        }
        // String validation for required fields
        else if (fieldType === 'string' && required) {
          validator = (value: any) => {
            // Check if this is a multilingual field (TextMultilingual object)
            if (isMultilingualFieldName(attr.name)) {
              // Handle TextMultilingual object
              if (isTextMultilingual(value)) {
                if (!value.en || typeof value.en !== 'string' || value.en.trim() === '') {
                  return `${attr.label} (English) is required`;
                }
                return null;
              }
              // If it's a multilingual field but value is not yet a TextMultilingual object,
              // check if it's an empty object or needs initialization
              if (value && typeof value === 'object' && !Array.isArray(value)) {
                // Empty object or object without 'en' property
                if (!value.en || typeof value.en !== 'string' || value.en.trim() === '') {
                  return `${attr.label} (English) is required`;
                }
                return null;
              }
              // If it's a string, that's also valid (will be converted to TextMultilingual)
              if (typeof value === 'string' && value.trim() !== '') {
                return null;
              }
              // Empty or invalid value
              return `${attr.label} (English) is required`;
            }
            // Regular string validation for non-multilingual fields
            if (typeof value !== 'string' || !value || value.trim() === '') {
              return `${attr.label} is required`;
            }
            return null;
          };
        }

        return {
          key: attr.name,
          label: attr.label || attr.name,
          type: fieldType,
          required,
          validator,
          minRows,
          maxRows,
          options,
          optionsReference,
          placeholder: (attr as any).placeholder
        };
      });

    return createFields;
  }, [attributes]);

  // Ensure attributes are loaded - can be called by EditActionButton
  const ensureAttributesLoaded = useCallback(async () => {
    // Don't fetch attributes if user is not authenticated (prevents 401 errors)
    const currentUser = getUserDataCache();
    if (!currentUser) {
      return [];
    }
    
    if (attributes && attributes.length > 0) {
      return attributes;
    }
    
    const fetchedAttributes = await fetchAttributesData();
    return fetchedAttributes;
  }, [attributes, fetchAttributesData]);

  // Fetch attributes and permissions on mount (only if user is authenticated)
  useEffect(() => {
    const currentUser = getUserDataCache();
    if (currentUser) {
      fetchAttributesData();
      fetchPermissions();
    }
  }, [fetchAttributesData, fetchPermissions]);

  // Initial fetch
  useEffect(() => {
    fetchRoles();
  }, [fetchRoles]);

  return {
    data: roles,
    loading,
    error,
    refetch: fetchRoles,
    removeOptimistically,
    updateOptimistically,
    attributes,
    permissions,
    pagination,
    fetchRoleById,
    generateEditFieldsFromAttributes,
    generateCreateFieldsFromAttributes,
    ensureAttributesLoaded
  };
}

// Role operations hook
export function useRbacRoleOperations() {
  const [deletingRoles, setDeletingRoles] = useState<Set<string>>(new Set());
  const [editingRoles, setEditingRoles] = useState<Set<string>>(new Set());
  const [creatingRole, setCreatingRole] = useState(false);
  const { request, isLoading } = useApiRequest();
  const [deleteError, setDeleteError] = useState<string | null>(null);
  const [createError, setCreateError] = useState<string | null>(null);
  const [updateError, setUpdateError] = useState<string | null>(null);

  const handleRoleDelete = async (roleId: string) => {
    setDeleteError(null);
    setDeletingRoles(prev => new Set(prev).add(roleId));
    
    try {
      await deleteRoleApi(request, roleId);
      
      await new Promise(resolve => setTimeout(resolve, 300));
      return true;
    } catch (error: any) {
      setDeleteError(error.message);
      return false;
    } finally {
      setDeletingRoles(prev => {
        const newSet = new Set(prev);
        newSet.delete(roleId);
        return newSet;
      });
    }
  };

  const handleRoleCreate = async (roleData: Partial<Role>) => {
    console.log('🟢 handleRoleCreate - Complete input structure:', {
      roleData,
      roleDataType: typeof roleData,
      roleDataIsArray: Array.isArray(roleData),
      roleDataIsNull: roleData === null,
      roleDataIsUndefined: roleData === undefined,
      roleDataKeys: roleData ? Object.keys(roleData) : [],
      roleDataEntries: roleData ? Object.entries(roleData).map(([key, value]) => ({
        key,
        value,
        valueType: typeof value,
        valueIsObject: typeof value === 'object' && value !== null && !Array.isArray(value),
        valueIsArray: Array.isArray(value),
        valueIsNull: value === null,
        valueIsUndefined: value === undefined,
        valueStringified: typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value)
      })) : [],
      roleDataStringified: JSON.stringify(roleData, null, 2),
      roleDataStringifiedCompact: JSON.stringify(roleData)
    });
    
    setCreateError(null);
    setCreatingRole(true);
    
    try {
      const newRole = await createRoleApi(request, roleData);
      console.log('✅ handleRoleCreate - Success, newRole:', newRole);
      
      return { success: true, roleData: newRole };
    } catch (error: any) {
      console.error('❌ handleRoleCreate - Error:', error);
      console.error('❌ handleRoleCreate - Complete error details:', {
        error,
        message: error.message,
        response: error.response,
        responseData: error.response?.data,
        responseStatus: error.response?.status,
        responseStatusText: error.response?.statusText,
        responseHeaders: error.response?.headers,
        request: error.request,
        config: error.config,
        stack: error.stack
      });
      setCreateError(error.message);
      return { success: false, error: error.message };
    } finally {
      setCreatingRole(false);
    }
  };

  const handleRoleUpdate = async (roleId: string, updateData: RoleUpdateData, _originalData?: any) => {
    setUpdateError(null);
    setEditingRoles(prev => new Set(prev).add(roleId));
    
    try {
      const updatedRole = await updateRoleApi(request, roleId, updateData);
      
      return { success: true, roleData: updatedRole };
    } catch (error: any) {
      const errorMessage = error.response?.data?.message || error.message || 'Failed to update role';
      const statusCode = error.response?.status;
      
      setUpdateError(errorMessage);
      
      return { 
        success: false, 
        error: errorMessage,
        statusCode,
        isPermissionError: statusCode === 403,
        isValidationError: statusCode === 400
      };
    } finally {
      setEditingRoles(prev => {
        const newSet = new Set(prev);
        newSet.delete(roleId);
        return newSet;
      });
    }
  };

  // Generic inline update handler for FormGeneratorTable
  const handleInlineUpdate = async (roleId: string, changes: Partial<RoleUpdateData>) => {
    const result = await handleRoleUpdate(roleId, changes as RoleUpdateData);
    if (!result.success) {
      throw new Error(result.error || 'Failed to update');
    }
    return result;
  };

  return {
    deletingRoles,
    editingRoles,
    creatingRole,
    deleteError,
    createError,
    updateError,
    handleRoleDelete,
    handleRoleCreate,
    handleRoleUpdate,
    handleInlineUpdate,
    isLoading
  };
}
