import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
import axios from 'axios';
import { motion, AnimatePresence } from 'framer-motion';
import { useTranslation } from 'react-i18next';
import { useAuth } from './AuthContext';
import { 
  Trophy, 
  Star, 
  Flame, 
  Target, 
  ShieldCheck, 
  Zap, 
  Map as MapIcon
} from 'lucide-react';

const iconMap = {
  Map: MapIcon,
  Zap,
  Flame,
  ShieldCheck,
  Trophy,
  Target
};

const AchievementNotificationContext = createContext();

export const useAchievementNotification = () => useContext(AchievementNotificationContext);

// Web Audio API procedural sound synthesizer for a nostalgic, satisfying chime
const playUnlockSound = () => {
  try {
    const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    const playTone = (freq, startTime, duration) => {
      const osc = audioCtx.createOscillator();
      const gain = audioCtx.createGain();
      
      osc.type = 'triangle'; // triangle wave gives a lovely clean bell/retro vibe
      osc.frequency.setValueAtTime(freq, startTime);
      
      gain.gain.setValueAtTime(0.12, startTime);
      gain.gain.exponentialRampToValueAtTime(0.0001, startTime + duration);
      
      osc.connect(gain);
      gain.connect(audioCtx.destination);
      
      osc.start(startTime);
      osc.stop(startTime + duration);
    };

    const now = audioCtx.currentTime;
    // Play a delightful rising major chord arpeggio: C5 -> E5 -> G5 -> C6
    playTone(523.25, now, 0.12); // C5
    playTone(659.25, now + 0.08, 0.12); // E5
    playTone(783.99, now + 0.16, 0.12); // G5
    playTone(1046.50, now + 0.24, 0.35); // C6
  } catch (e) {
    console.warn('[Audio] Failed to synthesize unlock chime:', e);
  }
};

const isResetDue = (lastResetTime, rule) => {
  if (!lastResetTime) return false;
  const lastReset = new Date(lastResetTime);
  const now = new Date();
  
  if (rule?.type === 'daily') {
    const [hour, minute] = (rule.time || '00:00').split(':').map(Number);
    const rDate = new Date(lastReset);
    rDate.setHours(hour, minute, 0, 0);
    
    if (lastReset.getTime() < rDate.getTime() && now.getTime() >= rDate.getTime()) {
      return true;
    }
    
    const tomorrowDate = new Date(rDate);
    tomorrowDate.setDate(tomorrowDate.getDate() + 1);
    if (lastReset.getTime() < tomorrowDate.getTime() && now.getTime() >= tomorrowDate.getTime()) {
      return true;
    }
  } else if (rule?.type === 'weekly') {
    const dayOfWeek = rule.day !== undefined ? Number(rule.day) : 1;
    const [hour, minute] = (rule.time || '00:00').split(':').map(Number);
    const rDate = new Date(lastReset);
    rDate.setHours(hour, minute, 0, 0);
    
    let nextReset = rDate;
    while (nextReset.getDay() !== dayOfWeek || nextReset.getTime() <= lastReset.getTime()) {
      nextReset.setDate(nextReset.getDate() + 1);
    }
    
    if (now.getTime() >= nextReset.getTime()) {
      return true;
    }
  } else if (rule?.type === 'hourly') {
    const hours = Number(rule.hours || 24);
    const nextReset = new Date(lastReset.getTime() + hours * 60 * 60 * 1000);
    if (now.getTime() >= nextReset.getTime()) {
      return true;
    }
  } else if (rule?.type === 'minutely') {
    const minutes = Number(rule.minutes || 5);
    const nextReset = new Date(lastReset.getTime() + minutes * 60 * 1000);
    if (now.getTime() >= nextReset.getTime()) {
      return true;
    }
  }
  return false;
};

const resetDownstreamNodesBackground = (startNodeId, nodes, edges) => {
  const visited = new Set();
  const queue = [startNodeId];
  
  while (queue.length > 0) {
    const currentId = queue.shift();
    const outEdges = edges.filter(e => e.source === currentId || e.Source === currentId);
    outEdges.forEach(edge => {
      const targetId = edge.target || edge.Target;
      if (targetId && !visited.has(targetId)) {
        visited.add(targetId);
        queue.push(targetId);
      }
    });
  }
  
  return nodes.map(node => {
    if (visited.has(node.id || node.Id)) {
      const nodeType = (node.type || node.Type || '').toLowerCase();
      if (nodeType === 'sketch' || nodeType === 'habit') {
        return {
          ...node,
          data: {
            ...node.data,
            status: 'Todo'
          }
        };
      }
    }
    return node;
  });
};

export const AchievementNotificationProvider = ({ children }) => {
  const { user } = useAuth();
  const { t } = useTranslation();
  const [activeNotification, setActiveNotification] = useState(null);
  const knownUnlockedIds = useRef(new Set());
  const isInitialized = useRef(false);
  const knownHabitStatuses = useRef(new Map());
  const isHabitsInitialized = useRef(false);
  const lastUserId = useRef(null);
  const isCheckingAchievements = useRef(false);
  const isCheckingHabits = useRef(false);
  const API_URL = import.meta.env.VITE_API_URL;

  const fetchCurrentAchievements = async () => {
    if (!user) return [];
    try {
      const response = await axios.get(`${API_URL}/achievements/progress`);
      
      const mapTitleToIcon = (title) => {
          if (title.includes('Canvas')) return 'Map';
          if (title.includes('Connect') || title.includes('Decision')) return 'ShieldCheck';
          if (title.includes('Zap')) return 'Zap';
          if (title.includes('Daily')) return 'Flame';
          if (title.includes('Clear Desk')) return 'Target';
          if (title.includes('Portrait')) return 'Camera';
          if (title.includes('Milestone')) return 'Target';
          return 'Trophy';
      };

      const mapCategoryToColor = (category) => {
        switch (category) {
          case 'Projects': return 'bg-blue-500/20';
          case 'Tasks': return 'bg-yellow-500/20';
          case 'Habits': return 'bg-orange-500/20';
          case 'System': return 'bg-gray-500/20';
          default: return 'bg-green-500/20';
        }
      };

      return response.data.map(item => ({
        id: item.achievementId || item.id,
        title: item.achievement?.title || 'Unknown Achievement',
        desc: item.achievement?.description || '',
        icon: item.achievement?.iconUrl || mapTitleToIcon(item.achievement?.title || ''),
        unlocked: item.isUnlocked,
        color: mapCategoryToColor(item.achievement?.category || ''),
        currentValue: item.currentValue,
        targetValue: item.achievement?.targetValue || 1
      }));
    } catch (e) {
      console.error('[Achievements] Error fetching progress:', e);
      return [];
    }
  };

  const getStoredUnlockedIds = (userId) => {
    try {
      const stored = localStorage.getItem(`unlocked_achievements_${userId}`);
      if (stored) {
        return new Set(JSON.parse(stored));
      }
    } catch (e) {
      console.warn('[Achievements] Error reading localStorage:', e);
    }
    return new Set();
  };

  const saveStoredUnlockedIds = (userId, idsSet) => {
    try {
      localStorage.setItem(`unlocked_achievements_${userId}`, JSON.stringify(Array.from(idsSet)));
    } catch (e) {
      console.warn('[Achievements] Error writing to localStorage:', e);
    }
  };

  const checkAchievements = async () => {
    if (!user || isCheckingAchievements.current) return;
    isCheckingAchievements.current = true;
    try {
      const achievements = await fetchCurrentAchievements();
      if (!achievements || achievements.length === 0) return;

      const userId = user.id || user.Id;
      if (!userId) return;

      // Load persistent cache from localStorage if memory is empty
      if (knownUnlockedIds.current.size === 0) {
        const storedIds = getStoredUnlockedIds(userId);
        storedIds.forEach(id => knownUnlockedIds.current.add(id));
      }

      // Separate checking based on whether we have completed the initial load
      if (!isInitialized.current) {
        // First load: just populate the set of already unlocked achievements
        achievements.forEach(ach => {
          if (ach.unlocked) {
            knownUnlockedIds.current.add(ach.id);
          }
        });
        saveStoredUnlockedIds(userId, knownUnlockedIds.current);
        isInitialized.current = true;
        console.log('[Achievements] Initialized unlocked cache with:', knownUnlockedIds.current.size, 'achievements.');
        return;
      }

      // Subsequent loads: check if any achievement is newly unlocked
      let cacheUpdated = false;
      for (const ach of achievements) {
        if (ach.unlocked && !knownUnlockedIds.current.has(ach.id)) {
          console.log('[Achievements] Newly Unlocked Achievement:', ach.title);
          knownUnlockedIds.current.add(ach.id);
          cacheUpdated = true;

          // Trigger the beautiful notification!
          triggerNotification({
            ...ach,
            isAchievement: true
          });
        }
      }

      if (cacheUpdated) {
        saveStoredUnlockedIds(userId, knownUnlockedIds.current);
      }
    } finally {
      isCheckingAchievements.current = false;
    }
  };

  const checkHabitCooldowns = async () => {
    if (!user || isCheckingHabits.current) return;
    isCheckingHabits.current = true;
    try {
      const graphsRes = await axios.get(`${API_URL}/graphs/user/${user.id}`);
      const graphs = graphsRes.data;

      let newlyRechargedHabits = [];

      for (const g of graphs) {
        try {
          const graphRes = await axios.get(`${API_URL}/graphs/${g.id}/full`);
          const originalNodes = graphRes.data?.nodes || [];
          const edges = graphRes.data?.edges || [];
          
          let hasChanges = false;
          let updatedNodes = [...originalNodes];

          for (const node of originalNodes) {
            const nodeType = (node.type || node.Type || '').toLowerCase();
            if (nodeType === 'habit') {
              const nodeId = node.id;
              const currentStatus = node.data?.status || 'Pending';
              
              if (!knownHabitStatuses.current.has(nodeId)) {
                knownHabitStatuses.current.set(nodeId, currentStatus);
                if (currentStatus === 'Done') {
                  const lastResetTime = node.data.lastResetTime || node.data.createdAt || new Date().toISOString();
                  const rule = node.data.resetRule || { type: 'daily', time: '00:00' };
                  if (isResetDue(lastResetTime, rule)) {
                    console.log(`[HabitTrackerBackground] Initial check found habit due: "${node.data.label}"`);
                    hasChanges = true;
                    updatedNodes = updatedNodes.map(n => {
                      if (n.id === nodeId) {
                        return {
                          ...n,
                          data: {
                            ...n.data,
                            status: 'Todo',
                            lastResetTime: new Date().toISOString()
                          }
                        };
                      }
                      return n;
                    });
                    updatedNodes = resetDownstreamNodesBackground(nodeId, updatedNodes, edges);
                    knownHabitStatuses.current.set(nodeId, 'Todo');
                    newlyRechargedHabits.push(node.data?.label || t('canvas.habit'));
                  }
                }
                continue;
              }

              const prevStatus = knownHabitStatuses.current.get(nodeId);

              if (currentStatus === 'Done') {
                const lastResetTime = node.data.lastResetTime || node.data.createdAt || new Date().toISOString();
                const rule = node.data.resetRule || { type: 'daily', time: '00:00' };
                
                if (isResetDue(lastResetTime, rule)) {
                  console.log(`[HabitTrackerBackground] Resetting habit: "${node.data.label}"`);
                  hasChanges = true;
                  
                  // 1. Reset target node
                  updatedNodes = updatedNodes.map(n => {
                    if (n.id === nodeId) {
                      return {
                        ...n,
                        data: {
                          ...n.data,
                          status: 'Todo',
                          lastResetTime: new Date().toISOString()
                        }
                      };
                    }
                    return n;
                  });

                  // 2. Reset downstream nodes
                  updatedNodes = resetDownstreamNodesBackground(nodeId, updatedNodes, edges);

                  knownHabitStatuses.current.set(nodeId, 'Todo');
                  newlyRechargedHabits.push(node.data?.label || t('canvas.habit'));
                } else {
                  knownHabitStatuses.current.set(nodeId, 'Done');
                }
              } else if (currentStatus === 'Pending' || currentStatus === 'Todo') {
                if (prevStatus === 'Done') {
                  console.log(`[HabitTrackerBackground] Habit transitioned to Todo/Pending externally: "${node.data.label}"`);
                  newlyRechargedHabits.push(node.data?.label || t('canvas.habit'));
                }
                knownHabitStatuses.current.set(nodeId, currentStatus);
              }
            }
          }

          if (hasChanges) {
            const payload = {
              nodes: updatedNodes,
              edges: edges
            };
            await axios.post(`${API_URL}/graphs/${g.id}/full`, payload);
            console.log(`[HabitTrackerBackground] Successfully persisted reset status for graph ${g.id}`);
          }
        } catch (e) {
          console.warn(`[HabitsBackground] Error checking/resetting graph ${g.id}:`, e);
        }
      }

      for (const name of newlyRechargedHabits) {
        triggerNotification({
          id: 'habit-recharge-' + Date.now() + '-' + Math.random(),
          icon: 'Flame',
          title: t('habits.recharge_notification_title', 'Habit Recharged! ⚡'),
          desc: t('habits.recharge_notification_desc', `Habit "${name}" is ready to be performed again!`, { name }),
          color: 'bg-orange-500/20',
          headerText: t('habits.recharge_notification_header', 'ЗВИЧКА ГОТОВА')
        });
      }
    } catch (e) {
      console.error('[HabitsBackground] Error fetching cooldown info:', e);
    } finally {
      isCheckingHabits.current = false;
    }
  };

  const triggerNotification = (achievement) => {
    playUnlockSound();
    setActiveNotification(achievement);

    const desktopNotifEnabled = localStorage.getItem('desktop_notifications') === 'true';
    if (desktopNotifEnabled && 'Notification' in window && Notification.permission === 'granted') {
      try {
        const titleText = achievement.isAchievement 
          ? `🏆 ${t('achievements.unlocked_header', 'Achievement Unlocked!')}: ${achievement.title}` 
          : `${achievement.title}`;
        new Notification(titleText, {
          body: achievement.desc,
          icon: '/logo.png'
        });
      } catch (e) {
        console.warn('[Notifications] Failed to display native desktop notification:', e);
      }
    }

    // Auto-clear after 6 seconds
    setTimeout(() => {
      setActiveNotification(null);
    }, 6000);
  };

  // Reset cache only if user logs out or switches to a different user account
  useEffect(() => {
    if (!user) {
      isInitialized.current = false;
      knownUnlockedIds.current.clear();
      knownHabitStatuses.current.clear();
      isHabitsInitialized.current = false;
      lastUserId.current = null;
      return;
    }

    if (user.id !== lastUserId.current) {
      console.log('[Achievements] User changed. Resetting unlocked achievements cache.');
      isInitialized.current = false;
      knownUnlockedIds.current.clear();
      knownHabitStatuses.current.clear();
      isHabitsInitialized.current = false;
      lastUserId.current = user.id;
      checkAchievements();
      checkHabitCooldowns();
    } else {
      // Same user updated (e.g. profile updated). Keep cache, just check for new achievements!
      checkAchievements();
      checkHabitCooldowns();
    }
  }, [user]);

  // Periodic polling check every 12 seconds
  useEffect(() => {
    if (!user) return;
    const interval = setInterval(() => {
      checkAchievements();
      checkHabitCooldowns();
    }, 12000);
    return () => clearInterval(interval);
  }, [user]);

  return (
    <AchievementNotificationContext.Provider value={{ checkAchievements, triggerNotification }}>
      {children}
      
      {/* Dynamic Overlay Achievement Notification Toast */}
      <AnimatePresence>
        {activeNotification && (
          <AchievementToast 
            achievement={activeNotification} 
            onClose={() => setActiveNotification(null)} 
          />
        )}
      </AnimatePresence>
    </AchievementNotificationContext.Provider>
  );
};

// High-fidelity, sketchy hand-drawn animated toast with procedural paper particles
const AchievementToast = ({ achievement, onClose }) => {
  const { t } = useTranslation();
  const Icon = iconMap[achievement.icon] || Star;

  // Pre-generate 30 vector particles for a sketchy paper-confetti explosion
  const confettiParticles = Array.from({ length: 30 }).map((_, i) => {
    const angle = Math.random() * 360;
    const distance = 90 + Math.random() * 130;
    return {
      id: i,
      angle,
      distance,
      size: 6 + Math.random() * 8,
      color: ['#F59E0B', '#10B981', '#3B82F6', '#EF4444', '#EC4899', '#8B5CF6'][Math.floor(Math.random() * 6)],
    };
  });

  return (
    <div className="fixed bottom-8 right-8 z-[9999] pointer-events-none flex flex-col items-center">
      {/* 1. Exploding Paper Confetti */}
      {confettiParticles.map((p) => {
        const radian = (p.angle * Math.PI) / 180;
        const targetX = Math.cos(radian) * p.distance;
        const targetY = Math.sin(radian) * p.distance;
        
        return (
          <motion.div
            key={p.id}
            className="absolute rounded-sm pointer-events-none"
            style={{ 
              backgroundColor: p.color,
              width: p.size,
              height: p.size,
              left: '50%',
              top: '50%',
              marginLeft: -p.size / 2,
              marginTop: -p.size / 2,
            }}
            initial={{ x: 0, y: 0, scale: 0, rotate: 0, opacity: 1 }}
            animate={{ 
              x: targetX, 
              y: targetY - 30, 
              scale: [0, 1.3, 0.9, 0],
              rotate: Math.random() * 720 - 360,
              opacity: [1, 1, 0]
            }}
            transition={{ 
              duration: 1.8,
              ease: [0.1, 0.8, 0.3, 1]
            }}
          />
        );
      })}

      {/* 2. Main Toast Body */}
      <motion.div
        initial={{ opacity: 0, y: 50, scale: 0.8, rotate: -3 }}
        animate={{ opacity: 1, y: 0, scale: 1, rotate: 0 }}
        exit={{ opacity: 0, y: 20, scale: 0.8, rotate: 3 }}
        transition={{ type: "spring", stiffness: 180, damping: 15 }}
        className="pointer-events-auto bg-surface-container-lowest p-6 rough-border border-4 border-primary shadow-[8px_8px_0px_0px_var(--color-primary)] flex items-center gap-6 max-w-md w-full relative overflow-hidden"
      >
        {/* Animated highlighter splash background */}
        <motion.div 
          initial={{ width: 0 }}
          animate={{ width: "100%" }}
          transition={{ delay: 0.4, duration: 0.6, ease: "easeOut" }}
          className="absolute inset-y-0 left-0 bg-secondary/10 -z-10 pointer-events-none"
        />

        {/* Unlocked Icon Box with wobble */}
        <motion.div
          animate={{ rotate: [0, -10, 10, -10, 10, 0] }}
          transition={{ delay: 0.5, duration: 0.8, ease: "easeInOut" }}
          className={`w-16 h-16 rounded-full flex items-center justify-center ${achievement.color || 'bg-secondary/20'} border-2 border-primary shadow-sm flex-shrink-0 relative`}
        >
          <Icon size={32} className="text-primary animate-pulse" />
          <div className="absolute -top-1 -right-1 bg-secondary text-primary rounded-full p-1 border border-primary">
            <Star size={10} fill="currentColor" />
          </div>
        </motion.div>

        {/* Description & Congratulations */}
        <div className="flex-grow flex flex-col gap-1">
          <div className="text-[10px] font-bold uppercase tracking-[0.2em] text-secondary font-display-lg">
            {achievement.isAchievement 
              ? t('achievements.unlocked_header', 'Achievement Unlocked!') 
              : (achievement.headerText || t('achievements.notification_header', 'Notification'))}
          </div>
          <h4 className="text-2xl font-display-lg text-primary leading-tight">
            {achievement.title}
          </h4>
          <p className="text-xs font-bold text-on-surface-variant/80 font-accent-note leading-relaxed">
            {achievement.desc}
          </p>
        </div>

        {/* Close Button */}
        <button 
          onClick={onClose}
          className="p-1 hover:bg-primary/5 rounded-full self-start hover:scale-110 transition-transform text-on-surface-variant hover:text-primary"
        >
          <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M6 18L18 6M6 6l12 12" />
          </svg>
        </button>
      </motion.div>
    </div>
  );
};
