
import React, { useState, useEffect } from 'react';
import { useIsAuthenticated, useMsal } from '@azure/msal-react';
import { InteractionStatus } from '@azure/msal-browser';
import { Hero } from './components/Hero';
import { analyzeProof } from './services/geminiService';
import { parseUrlState, pushUrlState } from './utils/urlState';
import { getUserInfo } from './services/authService';
import apiService from './services/apiService';
import type { AgentReview, AgentName, FlaggedItem, ResolvedItem, ErrorItem } from './types';
import { AGENT_NAMES } from './constants';
import { Sidebar } from './components/Sidebar';
import { ChecksOverview } from './components/ChecksOverview';
import { Analytics } from './components/Analytics';
import { Profile } from './components/Profile';
import { CopyGenAI } from './components/CopyGenAI';
import { Settings } from './components/Settings';
import { Campaigns } from './components/Campaigns';
import { Auditing } from './components/Auditing';
import { Login } from './components/Login';
import { WIPReviewer } from './components/WIPReviewer';
import { KnowledgeBase } from './components/KnowledgeBase';
import { UserManagement } from './components/UserManagement';
import { AgencyFilterBar } from './components/AgencyFilterBar';
import { UserProvider, useUser } from './contexts/UserContext';

type View = 'Home' | 'Analytics' | 'Campaigns' | 'WIP Reviewer' | 'CopyGenAI' | 'Settings' | 'Profile' | 'Auditing' | 'Knowledge Base' | 'User Management';

export interface DropdownOptions {
  campaigns: string[];
  // Hierarchy: Channel -> SubChannel -> ProofType[]
  channels: Record<string, Record<string, string[]>>;
  // Brand guidelines for campaign creation
  brandGuidelines: string[];
}

const App: React.FC = () => {
  // MSAL authentication state
  const isAuthenticated = useIsAuthenticated();
  const { instance: msalInstance, inProgress } = useMsal();

  // Initialize API service with MSAL instance BEFORE rendering UserProvider
  useEffect(() => {
    if (msalInstance) {
      apiService.setMsalInstance(msalInstance);
    }
  }, [msalInstance]);

  // Show loading spinner during MSAL authentication interactions
  if (inProgress !== InteractionStatus.None) {
    return (
      <div className="fixed inset-0 bg-oliver-black flex items-center justify-center">
        <div className="flex flex-col items-center gap-4">
          <svg className="animate-spin h-12 w-12 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
            <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
            <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          <p className="text-white/80 text-sm">Authenticating...</p>
        </div>
      </div>
    );
  }

  if (!isAuthenticated) {
    return <Login />;
  }

  return (
    <UserProvider>
      <AppContent msalInstance={msalInstance} />
    </UserProvider>
  );
};

const AppContent: React.FC<{ msalInstance: any }> = ({ msalInstance }) => {
  const isAuthenticated = true; // We're inside the authenticated boundary
  const { user, isLoading: isUserLoading, canWrite, canSeeAnalytics, canSeeAuditing, canSeeKnowledgeBase, canSeeSettings, canSeeUserManagement, canEditSettings, isSuperAdmin, isOversightAdmin, isUnassigned } = useUser();

  // Get initial state from URL
  const initialUrlState = parseUrlState();

  const [currentView, setCurrentView] = useState<View>(initialUrlState.view);
  const [selectedCampaign, setSelectedCampaign] = useState<string | null>(initialUrlState.campaignName);
  const [selectedProof, setSelectedProof] = useState<any | null>(null);
  const [pendingProofId, setPendingProofId] = useState<string | null>(initialUrlState.proofId);
  const [error, setErrorState] = useState<string | null>(null);
  const errorTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
  const setError = (msg: string | null) => {
    setErrorState(msg);
    if (errorTimerRef.current) clearTimeout(errorTimerRef.current);
    if (msg) errorTimerRef.current = setTimeout(() => setErrorState(null), 8000);
  };
  const [isLoadingData, setIsLoadingData] = useState(true);
  const [notification, setNotification] = useState<string | null>(null);
  const notificationTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);

  const showNotification = (message: string) => {
    setNotification(message);
    if (notificationTimerRef.current) clearTimeout(notificationTimerRef.current);
    notificationTimerRef.current = setTimeout(() => setNotification(null), 8000);
  };

  // Agency filter state (session-level, for oversight_admin / super_admin)
  const [selectedAgencyId, setSelectedAgencyId] = useState<string | null>(null);
  const showAgencyFilter = isSuperAdmin || isOversightAdmin;

  // Dropdown options now loaded from API
  const [dropdownOptions, setDropdownOptions] = useState<DropdownOptions>({
    campaigns: [],
    channels: {},
    brandGuidelines: []
  });

  // Load dropdown options from API when authenticated
  useEffect(() => {
    const loadDropdownOptions = async () => {
      if (!isAuthenticated) return;

      try {
        const options = await apiService.getDropdownOptions();
        console.log('[DEBUG App.tsx] Loaded dropdown options from API');
        console.log('[DEBUG App.tsx] options.channels:', JSON.stringify(options.channels, null, 2));
        console.log('[DEBUG App.tsx] Social.Meta proof types:', options.channels?.Social?.Meta);
        setDropdownOptions({
          campaigns: options.campaigns || [],
          channels: options.channels || {},
          brandGuidelines: options.brand_guidelines || []
        });
      } catch (error) {
        console.error('[DEBUG App.tsx] Failed to load dropdown options:', error);
        // Fall back to default options if API fails
        setDropdownOptions({
          campaigns: [],
          channels: {
            "Social": { "Meta": ["In-feed 1x1", "In-feed 4x5"] },
            "Display": { "Banner": ["300x600", "300x250"] }
          },
          brandGuidelines: ["Barclays", "Barclaycard"]
        });
      }
    };

    loadDropdownOptions();
  }, [isAuthenticated]);


  // Campaigns and proofs now loaded from API instead of localStorage
  const [campaigns, setCampaigns] = useState<any[]>([]);
  const [campaignProofs, setCampaignProofs] = useState<Record<string, any[]>>({});

  // Load campaigns from API when authenticated (re-fetch when agency filter changes)
  useEffect(() => {
    const loadCampaigns = async () => {
      if (!isAuthenticated || isUserLoading) return;

      setIsLoadingData(true);
      try {
        const response = await apiService.getCampaigns(selectedAgencyId || undefined);
        setCampaigns(response.map(c => apiService.convertCampaignToFrontend(c)));
      } catch (error) {
        console.error('Failed to load campaigns:', error);
        setError('Failed to load campaigns. Please try again.');
      } finally {
        setIsLoadingData(false);
      }
    };

    loadCampaigns();
  }, [isAuthenticated, isUserLoading, selectedAgencyId]);

   // Audit items now loaded from API instead of localStorage
   const [flaggedItems, setFlaggedItems] = useState<FlaggedItem[]>([]);
   const [resolvedItems, setResolvedItems] = useState<ResolvedItem[]>([]);
   const [errorItems, setErrorItems] = useState<ErrorItem[]>([]);

   // Load audit items from API when authenticated (re-fetch when agency filter changes)
   useEffect(() => {
     const loadAuditItems = async () => {
       if (!isAuthenticated || isUserLoading) return;

       try {
         const agencyFilter = selectedAgencyId || undefined;
         const [flagged, resolved, errors] = await Promise.all([
           apiService.getFlaggedItems(agencyFilter),
           apiService.getResolvedItems(agencyFilter),
           apiService.getErrorItems(agencyFilter),
         ]);
         setFlaggedItems(flagged.map(i => apiService.convertFlaggedItemToFrontend(i)));
         setResolvedItems(resolved.map(i => apiService.convertResolvedItemToFrontend(i)));
         setErrorItems(errors.map(i => apiService.convertErrorItemToFrontend(i)));
       } catch (error) {
         console.error('Failed to load audit items:', error);
       }
     };

     loadAuditItems();
   }, [isAuthenticated, isUserLoading, selectedAgencyId]);

  // Sync state changes to URL
  useEffect(() => {
    pushUrlState({
      view: currentView,
      campaignName: selectedCampaign,
      proofId: selectedProof?._id || null,
    });
  }, [currentView, selectedCampaign, selectedProof]);

  // Handle browser back/forward
  useEffect(() => {
    const handlePopState = () => {
      const state = parseUrlState();
      setCurrentView(state.view);
      setSelectedCampaign(state.campaignName);
      if (!state.proofId) setSelectedProof(null);
      setPendingProofId(state.proofId);
    };
    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, []);

  // Load proofs for campaign selected from URL (deep linking support)
  useEffect(() => {
    const loadProofsForUrlCampaign = async () => {
      if (!selectedCampaign || !isAuthenticated || campaigns.length === 0) return;
      // Skip if proofs already loaded for this campaign
      if (campaignProofs[selectedCampaign]) return;

      const campaign = campaigns.find(c => c.name === selectedCampaign);
      if (!campaign?._id) return;

      try {
        const proofs = await apiService.getProofs(campaign._id);
        setCampaignProofs(prev => ({
          ...prev,
          [selectedCampaign]: proofs.map(p => apiService.convertProofToFrontend(p))
        }));
      } catch (error) {
        console.error('Failed to load proofs for URL campaign:', selectedCampaign, error);
      }
    };

    loadProofsForUrlCampaign();
  }, [selectedCampaign, isAuthenticated, campaigns, campaignProofs]);

  // Restore proof selection from URL after proofs are loaded
  useEffect(() => {
    if (pendingProofId && selectedCampaign && campaignProofs[selectedCampaign]) {
      const proof = campaignProofs[selectedCampaign].find(p => p._id === pendingProofId);
      if (proof) {
        setSelectedProof(proof);
      }
      setPendingProofId(null);
    }
  }, [pendingProofId, selectedCampaign, campaignProofs]);

    useEffect(() => {
        // Keep selectedProof in sync with the master list in campaignProofs.
        // This ensures that when a new version is added, the detail view refreshes.
        if (selectedCampaign && selectedProof && campaignProofs[selectedCampaign]) {
            const currentCampaignProofs = campaignProofs[selectedCampaign];
            const freshProof = currentCampaignProofs.find(a => !a.tempId && a.proofName === selectedProof.proofName);
            
            // A simple stringify check to prevent re-renders if the object is the same.
            if (freshProof && JSON.stringify(freshProof) !== JSON.stringify(selectedProof)) {
                setSelectedProof(freshProof);
            }
        }
    }, [campaignProofs, selectedCampaign, selectedProof]);

  const handleAddNewCampaign = async (campaignData: { name: string; workfrontId: string; clientLead: string; agencyLead: string; brandGuidelines: string; }) => {
      try {
          const response = await apiService.createCampaign({
              name: campaignData.name,
              workfront_id: campaignData.workfrontId,
              client_lead: campaignData.clientLead,
              agency_lead: campaignData.agencyLead,
              brand_guidelines: campaignData.brandGuidelines,
          });

          const newCampaign = apiService.convertCampaignToFrontend(response);
          setCampaigns(prev => [...prev, newCampaign]);
          setCampaignProofs(prev => ({ ...prev, [newCampaign.name]: [] }));
      } catch (error) {
          console.error('Error creating campaign:', error);
          setError('Failed to create campaign. Please try again.');
      }
  };

  const fileToDataUrl = (file: File): Promise<string> => {
      return new Promise((resolve, reject) => {
          const reader = new FileReader();
          reader.onloadend = () => resolve(reader.result as string);
          reader.onerror = reject;
          reader.readAsDataURL(file);
      });
  };

    const handleProofUploadForCampaign = async (
        campaignName: string,
        file: File,
        proofName: string,
        channel: string,
        subChannel: string,
        proofType?: string
    ) => {
        setError(null);

        // Find the campaign to get its ID for API persistence
        const campaign = campaigns.find(c => c.name === campaignName);
        if (!campaign?._id) {
            setError('Campaign not found. Please refresh and try again.');
            return;
        }

        const tempId = `temp_${Date.now()}`;
        const newProofPlaceholder = {
            tempId,
            proofName,
            channel,
            subChannel,
            proofType,
            status: 'analyzing',
            analysisProgress: { completed: 0, total: AGENT_NAMES.length + 1 },
            file,
            versions: [],
        };

        setCampaignProofs(prevProofs => ({
            ...prevProofs,
            [campaignName]: [newProofPlaceholder, ...(prevProofs[campaignName] || [])]
        }));

        const handleAgentUpdate = (agentName: AgentName | 'Summary') => {
            setCampaignProofs(prevProofs => {
                const currentProofs = prevProofs[campaignName] || [];
                const updatedProofs = currentProofs.map(proof => {
                    if (proof.tempId === tempId && proof.status === 'analyzing') {
                        const newCompleted = (proof.analysisProgress?.completed ?? 0) + 1;
                        return { ...proof, analysisProgress: { ...proof.analysisProgress, completed: newCompleted } };
                    }
                    return proof;
                });
                return { ...prevProofs, [campaignName]: updatedProofs };
            });
        };

        try {
            // Pass campaign context to persist proof in database
            const result = await analyzeProof(file, handleAgentUpdate, msalInstance, {
                campaignId: campaign._id,
                proofName,
                channel,
                subChannel,
                proofType,
                brand: campaign.brandGuidelines,
            }, showNotification);

            const feedback = result.review;

            // Refresh proofs from API to get the persisted data
            // This ensures we have the correct IDs and data from the database
            try {
                const proofs = await apiService.getProofs(campaign._id);
                setCampaignProofs(prev => ({
                    ...prev,
                    [campaignName]: proofs.map(p => apiService.convertProofToFrontend(p))
                }));
            } catch (refreshError) {
                console.error('Failed to refresh proofs after analysis:', refreshError);
                // Fallback: remove the temp placeholder since analysis succeeded
                setCampaignProofs(prevProofs => ({
                    ...prevProofs,
                    [campaignName]: prevProofs[campaignName].filter(p => p.tempId !== tempId)
                }));
            }

            // Refresh campaigns list to get updated proof count
            try {
                const campaignsResponse = await apiService.getCampaigns();
                setCampaigns(campaignsResponse.map(c => apiService.convertCampaignToFrontend(c)));
            } catch (refreshError) {
                console.error('Failed to refresh campaigns after analysis:', refreshError);
            }

            // If analysis resulted in error, log it (error items are now tracked on backend)
            if (feedback.overallStatus === 'Analysis Error') {
                console.warn('Proof analysis resulted in error status:', feedback.leadAgentSummary);
            }

        } catch (err) {
            console.error("Failed to upload and analyze proof:", err);
            setError("Failed to upload and analyse proof. Please try again.");
            setCampaignProofs(prevProofs => ({
                ...prevProofs,
                [campaignName]: prevProofs[campaignName].map(proof =>
                    proof.tempId === tempId ? { ...proof, status: 'error' } : proof
                )
            }));
        }
    };

    const handleRetryAnalysis = async (campaignName: string, proofId: string) => {
        const matchProof = (proof: any) => proof.tempId === proofId || proof._id === proofId;
        const proofToRetry = campaignProofs[campaignName]?.find(matchProof);

        if (!proofToRetry) {
            console.error("Proof to retry not found");
            return;
        }

        // Find the campaign to get its ID for API persistence
        const campaign = campaigns.find(c => c.name === campaignName);
        if (!campaign?._id) {
            setError('Campaign not found. Please refresh and try again.');
            return;
        }

        let file = proofToRetry.file;

        // Fetch from backend if not in memory
        if (!file) {
            const storageKey = proofToRetry.fileStorageKey || proofToRetry.versions?.[0]?.fileStorageKey;
            if (!storageKey) {
                setError('Cannot retry: original file is not available.');
                return;
            }
            try {
                file = await apiService.getFile(storageKey);
            } catch (err) {
                console.error("Failed to fetch file for retry:", err);
                setError('Failed to retrieve original file for retry.');
                return;
            }
        }

        const { proofName, channel, subChannel, proofType } = proofToRetry;

        setCampaignProofs(prevProofs => ({
            ...prevProofs,
            [campaignName]: prevProofs[campaignName].map(proof =>
                matchProof(proof) ? { ...proof, status: 'analyzing', analysisProgress: { completed: 0, total: AGENT_NAMES.length + 1 } } : proof
            )
        }));

        const handleAgentUpdateForRetry = (agentName: AgentName | 'Summary') => {
            setCampaignProofs(prevProofs => {
                const currentProofs = prevProofs[campaignName] || [];
                const updatedProofs = currentProofs.map(proof => {
                    if (matchProof(proof) && proof.status === 'analyzing') {
                        const newCompleted = (proof.analysisProgress?.completed ?? 0) + 1;
                        return { ...proof, analysisProgress: { ...proof.analysisProgress, completed: newCompleted } };
                    }
                    return proof;
                });
                return { ...prevProofs, [campaignName]: updatedProofs };
            });
        };

        try {
            // Pass campaign context to persist proof in database
            const result = await analyzeProof(file, handleAgentUpdateForRetry, msalInstance, {
                campaignId: campaign._id,
                proofName,
                channel,
                subChannel,
                proofType,
                brand: campaign.brandGuidelines,
            }, showNotification);

            // Refresh proofs from API to get the persisted data
            try {
                const proofs = await apiService.getProofs(campaign._id);
                setCampaignProofs(prev => ({
                    ...prev,
                    [campaignName]: proofs.map(p => apiService.convertProofToFrontend(p))
                }));
            } catch (refreshError) {
                console.error('Failed to refresh proofs after retry:', refreshError);
                setCampaignProofs(prevProofs => ({
                    ...prevProofs,
                    [campaignName]: prevProofs[campaignName].filter(p => !matchProof(p))
                }));
            }

            // Refresh campaigns list to get updated proof count
            try {
                const campaignsResponse = await apiService.getCampaigns();
                setCampaigns(campaignsResponse.map(c => apiService.convertCampaignToFrontend(c)));
            } catch (refreshError) {
                console.error('Failed to refresh campaigns after retry:', refreshError);
            }

            if (result.review.overallStatus === 'Analysis Error') {
                console.warn('Retry analysis resulted in error status:', result.review.leadAgentSummary);
            }

        } catch (err) {
            console.error("Failed to retry proof analysis:", err);
            setCampaignProofs(prevProofs => ({
                ...prevProofs,
                [campaignName]: prevProofs[campaignName].map(proof =>
                    matchProof(proof) ? { ...proof, status: 'error' } : proof
                )
            }));
        }
    };

    const handleCampaignStatusChange = async (campaignName: string, newStatus: 'In Progress' | 'Completed') => {
        const campaign = campaigns.find(c => c.name === campaignName);
        if (!campaign?._id) {
            setError('Campaign not found.');
            return;
        }

        const previousStatus = campaign.status;
        const previousLastModified = campaign.lastModified;

        // Optimistically update local state immediately
        setCampaigns(prev => prev.map(p =>
            p.name === campaignName ? { ...p, status: newStatus, lastModified: new Date().toISOString() } : p
        ));

        try {
            await apiService.updateCampaign(campaign._id, { status: newStatus });
        } catch (error) {
            console.error('Error updating campaign status:', error);
            setError('Failed to update campaign status.');
            // Rollback on error
            setCampaigns(prev => prev.map(p =>
                p.name === campaignName ? { ...p, status: previousStatus, lastModified: previousLastModified } : p
            ));
        }
    };

    const handleDeleteProof = async (campaignName: string, proofName: string) => {
        // Find the proof to get its ID
        const proofToDelete = campaignProofs[campaignName]?.find(p => p.proofName === proofName);
        if (!proofToDelete?._id) {
            // If no _id, it might be a temp proof that hasn't been saved to DB yet
            // Just remove it from local state
            setCampaignProofs(prevProofs => ({
                ...prevProofs,
                [campaignName]: prevProofs[campaignName].filter(p => p.proofName !== proofName)
            }));
            return;
        }

        try {
            await apiService.deleteProof(proofToDelete._id);

            // Update local state after successful deletion
            setCampaignProofs(prevProofs => ({
                ...prevProofs,
                [campaignName]: prevProofs[campaignName].filter(p => p.proofName !== proofName)
            }));

            // Refresh campaigns to get updated proof count
            const campaignsResponse = await apiService.getCampaigns();
            setCampaigns(campaignsResponse.map(c => apiService.convertCampaignToFrontend(c)));
        } catch (error) {
            console.error('Error deleting proof:', error);
            setError('Failed to delete proof.');
        }
    };

    const handleDeleteCampaign = async (campaignName: string) => {
        const campaign = campaigns.find(c => c.name === campaignName);
        if (!campaign?._id) {
            setError('Campaign not found.');
            return;
        }

        try {
            await apiService.deleteCampaign(campaign._id);

            // Update local state after successful deletion
            setCampaigns(prev => prev.filter(c => c.name !== campaignName));
            setCampaignProofs(prev => {
                const newProofs = { ...prev };
                delete newProofs[campaignName];
                return newProofs;
            });
        } catch (error) {
            console.error('Error deleting campaign:', error);
            setError('Failed to delete campaign. Please try again.');
        }
    };

  // --- SETTINGS HANDLERS (NOW USE API) ---

  // Helper to refresh dropdown options from API
  const refreshDropdownOptions = async () => {
    try {
      const options = await apiService.getDropdownOptions();
      setDropdownOptions({
        campaigns: options.campaigns || [],
        channels: options.channels || {},
        brandGuidelines: options.brand_guidelines || []
      });
    } catch (error) {
      console.error('Failed to refresh dropdown options:', error);
    }
  };

  // Campaign options are not stored in dropdown_options table - keeping local for now
  const handleAddCampaignOption = (value: string) => {
     setDropdownOptions(prev => {
        if (!prev.campaigns.includes(value)) {
            return { ...prev, campaigns: [...prev.campaigns, value].sort() };
        }
        return prev;
     });
  };

  const handleRemoveCampaignOption = (value: string) => {
     setDropdownOptions(prev => ({ ...prev, campaigns: prev.campaigns.filter(c => c !== value) }));
  };

  const handleAddChannel = async (channel: string) => {
    try {
      await apiService.addChannel(channel);
      // Optimistically update local state
      setDropdownOptions(prev => ({
        ...prev,
        channels: { ...prev.channels, [channel]: {} }
      }));
    } catch (error) {
      console.error('Error adding channel:', error);
      setError('Failed to add channel.');
    }
  };

  const handleRemoveChannel = async (channel: string) => {
    try {
      await apiService.deleteChannel(channel);
      // Optimistically update local state
      setDropdownOptions(prev => {
        const newChannels = { ...prev.channels };
        delete newChannels[channel];
        return { ...prev, channels: newChannels };
      });
    } catch (error) {
      console.error('Error removing channel:', error);
      setError('Failed to remove channel.');
    }
  };

  const handleAddSubChannel = async (channel: string, subChannel: string) => {
    try {
      await apiService.addSubChannel(channel, subChannel);
      // Optimistically update local state
      setDropdownOptions(prev => ({
        ...prev,
        channels: {
          ...prev.channels,
          [channel]: { ...prev.channels[channel], [subChannel]: [] }
        }
      }));
    } catch (error) {
      console.error('Error adding sub-channel:', error);
      setError('Failed to add sub-channel.');
    }
  };

  const handleRemoveSubChannel = async (channel: string, subChannel: string) => {
    try {
      await apiService.deleteSubChannel(channel, subChannel);
      // Optimistically update local state
      setDropdownOptions(prev => {
        const channelData = { ...prev.channels[channel] };
        delete channelData[subChannel];
        return { ...prev, channels: { ...prev.channels, [channel]: channelData } };
      });
    } catch (error) {
      console.error('Error removing sub-channel:', error);
      setError('Failed to remove sub-channel.');
    }
  };

  const handleAddProofType = async (channel: string, subChannel: string, proofType: string) => {
    try {
      await apiService.addProofType(channel, subChannel, proofType);
      // Optimistically update local state
      setDropdownOptions(prev => {
        const channelData = prev.channels[channel] || {};
        const currentTypes = channelData[subChannel] || [];
        return {
          ...prev,
          channels: {
            ...prev.channels,
            [channel]: {
              ...channelData,
              [subChannel]: [...currentTypes, proofType].sort()
            }
          }
        };
      });
    } catch (error) {
      console.error('Error adding proof type:', error);
      setError('Failed to add proof type.');
    }
  };

  const handleRemoveProofType = async (channel: string, subChannel: string, proofType: string) => {
    try {
      await apiService.deleteProofType(channel, subChannel, proofType);
      // Optimistically update local state
      setDropdownOptions(prev => {
        const channelData = prev.channels[channel];
        if (!channelData) return prev;
        const currentTypes = channelData[subChannel] || [];
        return {
          ...prev,
          channels: {
            ...prev.channels,
            [channel]: {
              ...channelData,
              [subChannel]: currentTypes.filter(t => t !== proofType)
            }
          }
        };
      });
    } catch (error) {
      console.error('Error removing proof type:', error);
      setError('Failed to remove proof type.');
    }
  };


  const handleNavigate = (view: View) => {
    setCurrentView(view);
    setSelectedCampaign(null); // Reset campaign on any main navigation
    setSelectedProof(null);
  };

  const handleSelectCampaign = async (campaignName: string) => {
    setSelectedCampaign(campaignName);

    // Find the campaign to get its ID for API calls
    const campaign = campaigns.find(c => c.name === campaignName);
    if (!campaign?._id) {
      console.error('Campaign not found or missing ID:', campaignName);
      return;
    }

    // Load proofs from API if not already cached
    if (!campaignProofs[campaignName]) {
      try {
        const proofs = await apiService.getProofs(campaign._id);
        setCampaignProofs(prev => ({
          ...prev,
          [campaignName]: proofs.map(p => apiService.convertProofToFrontend(p))
        }));
      } catch (error) {
        console.error('Failed to load proofs for campaign:', campaignName, error);
        setError('Failed to load proofs. Please try again.');
      }
    }
  };

  const handleSelectProof = (proof: any) => {
    setSelectedProof(proof);
  };

  const handleBackToCampaignsList = () => {
    setSelectedCampaign(null);
    setSelectedProof(null);
  };

  const handleBackToCampaignDetails = () => {
    setSelectedProof(null);
  };

    const handleFlagSubmit = async (flagData: Omit<FlaggedItem, 'id' | 'timestamp' | 'submitter' | 'submitAgency'>) => {
        // Find the proof to get its ID
        const proof = campaignProofs[flagData.campaignName]?.find(p => p.proofName === flagData.proofName);
        if (!proof?._id) {
            setError('Proof not found. Unable to submit flag.');
            return;
        }

        try {
            const response = await apiService.flagProofVersion(
                proof._id,
                flagData.version,
                { agent_flagged: flagData.agentFlagged, comments: flagData.comments }
            );
            const newFlag = apiService.convertFlaggedItemToFrontend(response);
            setFlaggedItems(prev => [newFlag, ...prev]);
        } catch (error) {
            console.error('Error flagging proof version:', error);
            setError('Failed to submit flag. Please try again.');
        }
    };

    const handleResolveSubmit = async (resolveData: Omit<ResolvedItem, 'id' | 'timestamp' | 'submitter' | 'submitAgency'>) => {
        // Find the proof to get its ID
        const proof = campaignProofs[resolveData.campaignName]?.find(p => p.proofName === resolveData.proofName);
        if (!proof?._id) {
            setError('Proof not found. Unable to submit resolution.');
            return;
        }

        try {
            const response = await apiService.resolveProofVersion(
                proof._id,
                resolveData.version,
                { agent: resolveData.agent, issue: resolveData.issue, resolution: resolveData.resolution }
            );
            const newResolution = apiService.convertResolvedItemToFrontend(response);
            setResolvedItems(prev => [newResolution, ...prev]);
        } catch (error) {
            console.error('Error resolving proof version:', error);
            setError('Failed to submit resolution. Please try again.');
        }
    };


    const handleNavigateToAuditedItem = async (item: { campaignName: string; proofName: string; version: number }) => {
        // Use cached proofs or fetch from API (e.g. Oversight Admin going straight to Auditing)
        let proofs = campaignProofs[item.campaignName];
        if (!proofs) {
            const campaign = campaigns.find(c => c.name === item.campaignName);
            if (!campaign?._id) {
                alert(`Campaign "${item.campaignName}" not found. It may have been deleted.`);
                return;
            }
            try {
                const rawProofs = await apiService.getProofs(campaign._id);
                proofs = rawProofs.map(p => apiService.convertProofToFrontend(p));
                setCampaignProofs(prev => ({ ...prev, [item.campaignName]: proofs! }));
            } catch (error) {
                console.error('Failed to load proofs for campaign:', item.campaignName, error);
                alert('Failed to load proofs. Please try again.');
                return;
            }
        }

        const proofToSelect = proofs.find(a => a.proofName === item.proofName);
        if (proofToSelect) {
            const versionExists = proofToSelect.versions.some((v: any) => v.version === item.version);
            if (versionExists) {
                setSelectedCampaign(item.campaignName);
                setSelectedProof({ ...proofToSelect, initialVersion: item.version });
                setCurrentView('Campaigns');
            } else {
                alert(`Version ${item.version} not found for proof "${item.proofName}". It may have been deleted.`);
            }
        } else {
            alert(`Proof "${item.proofName}" not found in campaign "${item.campaignName}". It may have been deleted.`);
        }
    };

  const handleLogout = async () => {
    try {
      await msalInstance.logoutPopup({
        postLogoutRedirectUri: window.location.origin + import.meta.env.BASE_URL,
      });
    } catch (error) {
      console.error('Logout failed:', error);
    }
  };

  const readOnly = !canWrite || (isOversightAdmin && !user?.agencyId);

  const renderContent = () => {
    switch (currentView) {
      case 'Analytics':
        return <Analytics agencyId={selectedAgencyId || undefined} isAdmin={isSuperAdmin || isOversightAdmin} />;
      case 'Profile':
        return <Profile onLogout={handleLogout} msalInstance={msalInstance} />;
      case 'CopyGenAI':
        return <CopyGenAI />;
      case 'Campaigns':
        if (isUnassigned) {
          return (
            <div className="flex-1 flex items-center justify-center p-8">
              <div className="text-center max-w-md">
                <div className="text-5xl mb-4">🏢</div>
                <h2 className="text-xl font-semibold text-gray-700 mb-2">No Agency Assigned</h2>
                <p className="text-gray-500">
                  You are not currently assigned to an agency. Please contact your administrator to be assigned before you can view or create campaigns.
                </p>
              </div>
            </div>
          );
        }
        return <Campaigns
                  selectedCampaign={selectedCampaign}
                  selectedProof={selectedProof}
                  onSelectCampaign={handleSelectCampaign}
                  onSelectProof={handleSelectProof}
                  onBackToCampaignsList={handleBackToCampaignsList}
                  onBackToCampaignDetails={handleBackToCampaignDetails}
                  campaigns={campaigns}
                  campaignProofs={campaignProofs}
                  onAddNewCampaign={handleAddNewCampaign}
                  onProofUpload={handleProofUploadForCampaign}
                  dropdownOptions={dropdownOptions}
                  onRetryAnalysis={handleRetryAnalysis}
                  onCampaignStatusChange={handleCampaignStatusChange}
                  onDeleteProof={handleDeleteProof}
                  onDeleteCampaign={handleDeleteCampaign}
                  onFlagSubmit={handleFlagSubmit}
                  onResolveSubmit={handleResolveSubmit}
                  flaggedItems={flaggedItems}
                  resolvedItems={resolvedItems}
                  readOnly={readOnly || isUnassigned}
               />;
      case 'WIP Reviewer':
        return <WIPReviewer dropdownOptions={dropdownOptions} msalInstance={msalInstance} />;
      case 'Auditing':
        return <Auditing
                    flaggedItems={flaggedItems}
                    resolvedItems={resolvedItems}
                    errorItems={errorItems}
                    onNavigate={handleNavigateToAuditedItem}
                />;
      case 'Knowledge Base':
        return <KnowledgeBase />;
      case 'User Management':
        return <UserManagement />;
      case 'Settings':
        return <Settings
                  options={dropdownOptions}
                  onAddCampaign={handleAddCampaignOption}
                  onRemoveCampaign={handleRemoveCampaignOption}
                  onAddChannel={handleAddChannel}
                  onRemoveChannel={handleRemoveChannel}
                  onAddSubChannel={handleAddSubChannel}
                  onRemoveSubChannel={handleRemoveSubChannel}
                  onAddProofType={handleAddProofType}
                  onRemoveProofType={handleRemoveProofType}
                  readOnly={!canEditSettings}
                />;
      case 'Home':
      default:
        return (
          <>
            <Hero onGetStarted={() => handleNavigate('Campaigns')} />
            {/* Hidden per Oliver design — client may want it back */}
            {/* <ChecksOverview /> */}
          </>
        );
    }
  };

  // Show loading spinner while user profile is loading
  if (isUserLoading) {
    return (
      <div className="fixed inset-0 bg-oliver-black flex items-center justify-center">
        <div className="flex flex-col items-center gap-4">
          <svg className="animate-spin h-12 w-12 text-oliver-azure" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
            <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
            <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          <p className="text-white/80 text-sm">Loading user profile...</p>
        </div>
      </div>
    );
  }

  // Determine background color based on view to avoid grey bar on Home view
  const mainBgColor = currentView === 'Home' ? 'bg-white' : 'bg-oliver-grey';

  // Get user info from MSAL for sidebar display
  const userInfo = getUserInfo(msalInstance);

  return (
    <div className={`flex h-screen ${mainBgColor} font-sans text-gray-800 overflow-hidden min-w-[900px]`}>
      <Sidebar
        activeItem={currentView}
        onNavigate={(view) => handleNavigate(view as View)}
        userName={user?.name || userInfo?.name}
        userEmail={user?.email || userInfo?.email}
        userRole={user?.role || 'basic_user'}
      />
      <div className="flex-1 flex flex-col overflow-y-auto">
        {showAgencyFilter && (
          <AgencyFilterBar
            selectedAgencyId={selectedAgencyId}
            onAgencyChange={setSelectedAgencyId}
          />
        )}
        <main className="flex-1 flex flex-col min-h-full">
          {renderContent()}
        </main>
      </div>

      {/* Error toast */}
      {error && (
        <div className="fixed top-6 left-1/2 -translate-x-1/2 z-50 flex items-start gap-3 bg-error text-white text-sm px-5 py-3.5 rounded-xl shadow-2xl max-w-md w-full mx-4 animate-fade-in">
          <svg className="w-5 h-5 shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
          </svg>
          <p className="flex-1 leading-snug">{error}</p>
          <button onClick={() => setErrorState(null)} className="text-white/70 hover:text-white shrink-0">
            <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
              <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        </div>
      )}

      {/* Model fallback notification toast */}
      {notification && (
        <div className="fixed bottom-6 left-1/2 -translate-x-1/2 z-50 flex items-start gap-3 bg-oliver-orange text-oliver-black text-sm px-5 py-3.5 rounded-xl shadow-2xl max-w-md w-full mx-4 animate-fade-in">
          <svg className="w-5 h-5 text-oliver-black shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
          </svg>
          <div className="flex-1">
            <p className="font-semibold text-oliver-black mb-0.5">AI Model Notice</p>
            <p className="text-oliver-black leading-snug">{notification}</p>
          </div>
          <button onClick={() => setNotification(null)} className="text-oliver-black/60 hover:text-oliver-black shrink-0">
            <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
              <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        </div>
      )}
    </div>
  );
};

export default App;
