import React, { createContext, useContext, useState, ReactNode, useCallback, useEffect } from 'react';
import { User, Message, Conversation, contactToUser, Department } from '../types';
// Removed mock data imports - using real API data only
import { dashboardApi, DashboardStats, AnalyticsData, contactApi, departmentsApi, organizationalApi, Unit } from '../services/api';
import { requestThrottler, apiCache } from '../utils/requestThrottler';

interface AppContextType {
  users: User[];
  messages: Message[];
  conversations: Conversation[];
  selectedConversation: Conversation | null;
  searchQuery: string;
  dashboardStats: DashboardStats | null;
  dashboardLoading: boolean;
  dashboardError: string | null;
  analyticsData: AnalyticsData | null;
  analyticsLoading: boolean;
  analyticsError: string | null;
  contactsLoading: boolean;
  contactsError: string | null;
  contactsTotal: number;
  departments: Department[];
  departmentsLoading: boolean;
  departmentsError: string | null;
  units: Unit[];
  unitsLoading: boolean;
  unitsError: string | null;
  setSearchQuery: (query: string) => void;
  setSelectedConversation: (conversation: Conversation | null) => void;
  sendMessage: (content: string, receiverId?: number, groupId?: number) => void;
  markMessageAsRead: (messageId: number) => void;
  addUser: (user: Omit<User, 'id'>) => void;
  updateUser: (id: number, user: Partial<User>) => void;
  deleteUser: (id: number) => void;
  createOrFindConversation: (userId: number) => Conversation;
  fetchDashboardStats: () => Promise<void>;
  fetchAnalytics: () => Promise<void>;
  fetchContacts: (params?: {
    page?: number;
    limit?: number;
    search?: string;
    contact_type?: 'internal' | 'external';
    parent_id?: number;
    unit_id?: number;
    branch_id?: number;
    tree?: boolean;
    include_inactive?: boolean;
    role_filter?: string;
    forceRefresh?: boolean;
  }) => Promise<void>;
  createContact: (contactData: any) => Promise<void>;
  updateContact: (id: number, contactData: any) => Promise<void>;
  deleteContact: (
    id: number,
    options?: { source?: 'management' | 'directory'; scope?: 'department' | 'all'; department_id?: number }
  ) => Promise<void>;
  fetchDepartments: () => Promise<void>;
  deleteDepartment: (id: number) => Promise<void>;
  fetchUnits: (useTree?: boolean) => Promise<void>;
}

const AppContext = createContext<AppContextType | undefined>(undefined);

export const useApp = () => {
  const context = useContext(AppContext);
  if (context === undefined) {
    throw new Error('useApp must be used within an AppProvider');
  }
  return context;
};

interface AppProviderProps {
  children: ReactNode;
}

export const AppProvider: React.FC<AppProviderProps> = ({ children }) => {
  const [users, setUsers] = useState<User[]>([]);
  const [messages, setMessages] = useState<Message[]>([]);
  const [conversations, setConversations] = useState<Conversation[]>([]);
  const [selectedConversation, setSelectedConversation] = useState<Conversation | null>(null);
  const [searchQuery, setSearchQuery] = useState<string>('');
  const [dashboardStats, setDashboardStats] = useState<DashboardStats | null>(null);
  const [dashboardLoading, setDashboardLoading] = useState<boolean>(false);
  const [dashboardError, setDashboardError] = useState<string | null>(null);
  const [analyticsData, setAnalyticsData] = useState<AnalyticsData | null>(null);
  const [analyticsLoading, setAnalyticsLoading] = useState<boolean>(false);
  const [analyticsError, setAnalyticsError] = useState<string | null>(null);
  const [contactsLoading, setContactsLoading] = useState<boolean>(false);
  const [contactsError, setContactsError] = useState<string | null>(null);
  const [contactsTotal, setContactsTotal] = useState<number>(0);
  const [departments, setDepartments] = useState<Department[]>([]);
  const [departmentsLoading, setDepartmentsLoading] = useState<boolean>(false);
  const [departmentsError, setDepartmentsError] = useState<string | null>(null);
  const [units, setUnits] = useState<Unit[]>([]);
  const [unitsLoading, setUnitsLoading] = useState<boolean>(false);
  const [unitsError, setUnitsError] = useState<string | null>(null);

  const sendMessage = (content: string, receiverId?: number, groupId?: number) => {
    const newMessage: Message = {
      id: Date.now(),
      senderId: 1, // Current user ID
      receiverId,
      groupId,
      content,
      timestamp: new Date(),
      type: 'text',
      read: false
    };

    setMessages(prev => [...prev, newMessage]);
  };

  const markMessageAsRead = (messageId: number) => {
    setMessages(prev => 
      prev.map(msg => 
        msg.id === messageId ? { ...msg, read: true } : msg
      )
    );
  };

  const addUser = (userData: Omit<User, 'id'>) => {
    const newUser: User = {
      ...userData,
      id: Math.max(...users.map(u => u.id), 0) + 1
    };
    setUsers(prev => [...prev, newUser]);
  };

  const updateUser = (id: number, userData: Partial<User>) => {
    setUsers(prev => 
      prev.map(user => 
        user.id === id ? { ...user, ...userData } : user
      )
    );
  };

  const deleteUser = (id: number) => {
    setUsers(prev => prev.filter(user => user.id !== id));
  };

  const createOrFindConversation = (userId: number) => {
    // Check if conversation already exists
    const existingConversation = conversations.find(conv => 
      conv.type === 'direct' && 
      conv.participants.includes(1) && // Current user
      conv.participants.includes(userId)
    );

    if (existingConversation) {
      return existingConversation;
    }

    // Create new conversation
    const newConversation: Conversation = {
      id: Math.max(...conversations.map(c => c.id), 0) + 1,
      type: 'direct',
      participants: [1, userId], // Current user and target user
      unreadCount: 0
    };

    setConversations(prev => [...prev, newConversation]);
    return newConversation;
  };

  const fetchDashboardStats = useCallback(async () => {
    setDashboardLoading(true);
    setDashboardError(null);
    
    try {
      const response = await dashboardApi.getStats();
      console.log('Dashboard API response:', response);
      
      if (response.status === 'success' && response.data) {
        setDashboardStats(response.data);
        console.log('Dashboard stats loaded successfully');
      } else {
        console.log('Failed to set dashboard stats. Status:', response.status);
        setDashboardError(response.message || 'Failed to fetch dashboard stats');
      }
    } catch (error) {
      console.error('Failed to fetch dashboard stats:', error);
      setDashboardError(error instanceof Error ? error.message : 'Failed to fetch dashboard stats');
    } finally {
      setDashboardLoading(false);
    }
  }, []); // Empty dependency array since we don't depend on any state

  const fetchAnalytics = useCallback(async () => {
    setAnalyticsLoading(true);
    setAnalyticsError(null);
    
    try {
      const response = await dashboardApi.getAnalytics();
      console.log('Analytics API response:', response);
      
      if (response.status === 'success' && response.data) {
        setAnalyticsData(response.data);
        console.log('Analytics data set:', response.data);
      } else {
        setAnalyticsError(response.message || 'Failed to fetch analytics data');
      }
    } catch (error) {
      console.error('Failed to fetch analytics data:', error);
      setAnalyticsError(error instanceof Error ? error.message : 'Failed to fetch analytics data');
    } finally {
      setAnalyticsLoading(false);
    }
  }, []); // Empty dependency array since we don't depend on any state

  const fetchContacts = useCallback(async (params = {}) => {
    const { forceRefresh, ...queryParams } = params as any;
    const cacheKey = `contacts_${JSON.stringify(queryParams)}`;
    
    // Check cache first
    const cachedData = apiCache.get(cacheKey);
    if (!forceRefresh && cachedData) {
      console.log('Using cached contacts data');
      setUsers(cachedData);
      setContactsLoading(false);
      return;
    }

    setContactsLoading(true);
    setContactsError(null);
    
    try {
      // Throttle the request with longer delay
      const response = await requestThrottler.throttle(
        'fetchContacts',
        () => contactApi.getContacts({ ...queryParams }), // Use params as-is, don't override tree
        3000 // 3 second delay between requests to prevent rate limiting
      );
      console.log('Contacts API response:', response);
      
      if (response.status === 'success' && response.data) {
        const contacts = response.data.contacts.map(contactToUser);
        
        // Keep backend order (latest first). Sort by ID in descending order
        const sortedContacts = contacts.sort((a, b) => b.id - a.id);
        
        // Use only API data - no mock data fallback
        setUsers(sortedContacts);
        const totalFromStats = (response.data.statistics && typeof response.data.statistics.total_contacts === 'number')
          ? response.data.statistics.total_contacts
          : sortedContacts.length;
        setContactsTotal(totalFromStats);
        // Cache the result
        apiCache.set(cacheKey, sortedContacts);
        console.log('Contacts loaded successfully from database:', sortedContacts.length);
        console.log('Available units in database:', [...new Set(sortedContacts.map(c => c.unit))]);
      } else {
        setContactsError(response.message || 'Failed to fetch contacts');
      }
    } catch (error) {
      console.error('Failed to fetch contacts:', error);
      // Check if it's a rate limiting error
      if (error instanceof Error && error.message.includes('Too many requests')) {
        setContactsError('Rate limit exceeded. Please wait a moment and try again.');
      } else if (error instanceof Error && error.message.includes('Access token')) {
        setContactsError('Authentication required. Please log in again.');
      } else {
        setContactsError(error instanceof Error ? error.message : 'Failed to fetch contacts');
      }
    } finally {
      setContactsLoading(false);
    }
  }, []);

  const createContact = useCallback(async (contactData: any) => {
    try {
      const response = await contactApi.createContact(contactData);
      if (response.status === 'success' && response.data) {
        const newUser = contactToUser(response.data.contact);
        setUsers(prev => [...prev, newUser]);
        console.log('Contact created successfully');
        // Force refresh full contacts list so view shows latest data
        await fetchContacts({
          tree: false,
          limit: 1000,
          include_inactive: true,
          forceRefresh: true
        });
      } else {
        throw new Error(response.message || 'Failed to create contact');
      }
    } catch (error) {
      console.error('Failed to create contact:', error);
      throw error;
    }
  }, []);

  const updateContact = useCallback(async (id: number, contactData: any) => {
    try {
      const response = await contactApi.updateContact(id, contactData);
      if (response.status === 'success' && response.data) {
        const updatedUser = contactToUser(response.data.contact);
        setUsers(prev => prev.map(user => user.id === id ? updatedUser : user));
        console.log('Contact updated successfully');
        // Force refresh full contacts list so view shows latest data
        await fetchContacts({
          tree: false,
          limit: 1000,
          include_inactive: true,
          forceRefresh: true
        });
      } else {
        throw new Error(response.message || 'Failed to update contact');
      }
    } catch (error) {
      console.error('Failed to update contact:', error);
      throw error;
    }
  }, []);

  const deleteContact = useCallback(async (id: number, options?: { source?: 'management' | 'directory'; scope?: 'department' | 'all'; department_id?: number }) => {
    try {
      const response = await contactApi.deleteContact(id, options);
      if (response.status === 'success') {
        setUsers(prev => prev.filter(user => user.id !== id));
        console.log('Contact deleted successfully');
        // Force refresh full contacts list so view shows latest data
        await fetchContacts({
          tree: false,
          limit: 1000,
          include_inactive: true,
          forceRefresh: true
        });
      } else {
        throw new Error(response.message || 'Failed to delete contact');
      }
    } catch (error) {
      console.error('Failed to delete contact:', error);
      throw error;
    }
  }, []);

  const fetchDepartments = useCallback(async () => {
    const cacheKey = 'departments';
    
    // Check cache first
    const cachedData = apiCache.get(cacheKey);
    if (cachedData) {
      console.log('Using cached departments data');
      setDepartments(cachedData);
      setDepartmentsLoading(false);
      return;
    }

    setDepartmentsLoading(true);
    setDepartmentsError(null);
    
    try {
      // Throttle the request with longer delay
      const response = await requestThrottler.throttle(
        'fetchDepartments',
        () => departmentsApi.getDepartmentsForDropdown(),
        3000 // 3 second delay to prevent rate limiting
      );
      
      console.log('Departments API response:', response);
      
      if (response.status === 'success' && response.data) {
        setDepartments(response.data);
        // Cache the result
        apiCache.set(cacheKey, response.data);
        console.log('Departments loaded successfully:', response.data.length);
      } else {
        setDepartmentsError(response.message || 'Failed to fetch departments');
      }
    } catch (error) {
      console.error('Failed to fetch departments:', error);
      // Check if it's a rate limiting error
      if (error instanceof Error && error.message.includes('Too many requests')) {
        setDepartmentsError('Rate limit exceeded. Please wait a moment and try again.');
      } else if (error instanceof Error && error.message.includes('Access token')) {
        setDepartmentsError('Authentication required. Please log in again.');
      } else {
        setDepartmentsError(error instanceof Error ? error.message : 'Failed to fetch departments');
      }
    } finally {
      setDepartmentsLoading(false);
    }
  }, []);

  const deleteDepartment = useCallback(async (id: number) => {
    try {
      const response = await departmentsApi.deleteDepartment(id);
      if (response.status === 'success') {
        // Remove the department from the local state
        setDepartments(prev => prev.filter(dept => dept.id !== id));
        console.log('Department deleted successfully');
      } else {
        throw new Error(response.message || 'Failed to delete department');
      }
    } catch (error) {
      console.error('Failed to delete department:', error);
      throw error;
    }
  }, []);

  const fetchUnits = useCallback(async (useTree: boolean = false) => {
    setUnitsLoading(true);
    setUnitsError(null);
    
    try {
      const response = await organizationalApi.getUnits({ tree: useTree });
      console.log('Units API response:', response);
      
      if (response.status === 'success' && response.data) {
        setUnits(response.data.units);
        console.log('Units loaded successfully:', response.data.units.length);
      } else {
        setUnitsError(response.message || 'Failed to fetch units');
      }
    } catch (error) {
      console.error('Failed to fetch units:', error);
      setUnitsError(error instanceof Error ? error.message : 'Failed to fetch units');
    } finally {
      setUnitsLoading(false);
    }
  }, []);

  // Fetch contacts, departments, and units on component mount
  useEffect(() => {
    fetchContacts();
    fetchDepartments();
    fetchUnits();
  }, [fetchContacts, fetchDepartments, fetchUnits]);

  const value = {
    users,
    messages,
    conversations,
    selectedConversation,
    searchQuery,
    dashboardStats,
    dashboardLoading,
    dashboardError,
    analyticsData,
    analyticsLoading,
    analyticsError,
    contactsLoading,
    contactsError,
    contactsTotal,
    departments,
    departmentsLoading,
    departmentsError,
    units,
    unitsLoading,
    unitsError,
    setSearchQuery,
    setSelectedConversation,
    sendMessage,
    markMessageAsRead,
    addUser,
    updateUser,
    deleteUser,
    createOrFindConversation,
    fetchDashboardStats,
    fetchAnalytics,
    fetchContacts,
    createContact,
    updateContact,
    deleteContact,
    fetchDepartments,
    deleteDepartment,
    fetchUnits
  };

  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
};
