import { useState, useEffect } from "react";
import { KioskClient } from "./client";
import type { Content, Template, KioskConfig, CacheOptions, SyncOptions, TemplateValues } from "./types";

const CACHE_KEY_PREFIX = 'kiosk_content_cache_';
const CACHE_TIMESTAMP_KEY = 'kiosk_content_last_sync';

function getCacheKey(projectId: string) {
  return `${CACHE_KEY_PREFIX}${projectId}`;
}

// Helper function to find field by id, consistent with client.ts
function findFieldById(fields: Template['fields'], fieldId: string) {
  return fields.find(field => field.id === fieldId);
}

function processTemplateContent(template: Template, content: Content): Content {
  if (!template) return content;

  console.log("Processing content with template:", {
    contentId: content.id,
    templateId: template.id,
    templateValues: content.templateValues
  });

  // Start with existing template values if available
  const templateValues: TemplateValues = content.templateValues || {
    groups: {},
    ungrouped: {}
  };

  // Process fields in groups
  Object.entries(template.groups || {}).forEach(([_, group]) => {
    if (!group || !group.normalizedName) {
      console.error(`Group ${group?.name ?? 'unknown'} is missing normalizedName`);
      return;
    }

    const normalizedName = group.normalizedName;
    console.log(`Processing group ${group.name} -> ${normalizedName}`);

    // Initialize group if it doesn't exist
    if (!templateValues.groups[normalizedName]) {
      templateValues.groups[normalizedName] = {};
    }

    // Process fields in this group
    (group.fieldIds || []).forEach(fieldId => {
      const field = findFieldById(template.fields, fieldId);
      if (field?.name) {
        // Only set default value if the field doesn't exist
        if (templateValues.groups[normalizedName][field.name] === undefined) {
          templateValues.groups[normalizedName][field.name] = field.defaultValue ?? null;
        }
      }
    });
  });

  // Process ungrouped fields
  (template.ungroupedFieldIds || []).forEach(fieldId => {
    const field = findFieldById(template.fields, fieldId);
    if (field?.name) {
      // Only set default value if the field doesn't exist
      if (templateValues.ungrouped[field.name] === undefined) {
        templateValues.ungrouped[field.name] = field.defaultValue ?? null;
      }
    }
  });

  console.log("Processed template values:", templateValues);

  return {
    ...content,
    templateValues
  };
}

function isOffline(): boolean {
  return !navigator.onLine;
}

function saveToCache(projectId: string, data: Content[]) {
  try {
    const cacheData = {
      timestamp: Date.now(),
      content: data
    };
    localStorage.setItem(getCacheKey(projectId), JSON.stringify(cacheData));
    localStorage.setItem(CACHE_TIMESTAMP_KEY, Date.now().toString());
  } catch (err) {
    console.warn('Failed to save to cache:', err);
  }
}

function getFromCache(projectId: string): { content: Content[]; timestamp: number } | null {
  try {
    const cached = localStorage.getItem(getCacheKey(projectId));
    if (!cached) return null;

    const parsed = JSON.parse(cached);
    if (!parsed || !parsed.content || !Array.isArray(parsed.content)) {
      return null;
    }

    return {
      content: parsed.content,
      timestamp: parsed.timestamp || 0
    };
  } catch (err) {
    console.warn('Failed to read from cache:', err);
    return null;
  }
}

function getCacheTimestamp(): number {
  return parseInt(localStorage.getItem(CACHE_TIMESTAMP_KEY) || '0', 10);
}

function isCacheValid(timestamp: number, maxAge: number): boolean {
  return Date.now() - timestamp <= maxAge;
}

function isCacheStale(maxAge: number): boolean {
  const lastSync = getCacheTimestamp();
  return Date.now() - lastSync > maxAge;
}


function filterPublishedProjectContent(content: Content[]): Content[] {
  return content.filter(item =>
    item.status === "published" &&
    item.projectIds &&
    item.projectIds.length > 0
  );
}

export function useKioskContent(config: KioskConfig) {
  const [content, setContent] = useState<Content[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isSyncing, setIsSyncing] = useState(false);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let isMounted = true;

    async function fetchContentAndTemplates() {
      if (!isOnline && config.offlineSupport) {
        const cached = getFromCache(config.projectId);
        if (cached) {
          const templates = await client.getTemplates().catch(() => []);
          const processedContent = filterPublishedProjectContent(cached.content).map(item => {
            if (item.templateId) {
              const template = templates.find(t => t.id === item.templateId);
              if (template) {
                return processTemplateContent(template, item);
              }
            }
            return item;
          });

          if (isMounted) {
            setContent(processedContent);
            setLoading(false);
          }
          return;
        }
      }

      try {
        setIsSyncing(true);
        const [contentData, templates] = await Promise.all([
          client.getPublishedContent(),
          client.getTemplates()
        ]);

        if (isMounted) {
          console.log("Raw content data:", contentData);
          console.log("Available templates:", templates);

          const processedContent = filterPublishedProjectContent(contentData).map(item => {
            if (item.templateId) {
              const template = templates.find(t => t.id === item.templateId);
              if (template) {
                console.log("Processing content with template:", {
                  contentId: item.id,
                  templateId: template.id,
                  beforeProcess: item.templateValues
                });

                const processed = processTemplateContent(template, item);
                console.log("After processing:", processed.templateValues);
                return processed;
              }
            }
            return item;
          });

          console.log("Final processed content:", processedContent);
          setContent(processedContent);
          if (config.offlineSupport) {
            saveToCache(config.projectId, processedContent);
          }
        }
      } catch (err) {
        console.error("Error fetching content:", err);
        if (isMounted) {
          if (config.offlineSupport) {
            const cached = getFromCache(config.projectId);
            if (cached) {
              setContent(filterPublishedProjectContent(cached.content));
            } else {
              setError(err instanceof Error ? err : new Error('Failed to fetch content'));
            }
          } else {
            setError(err instanceof Error ? err : new Error('Failed to fetch content'));
          }
        }
      } finally {
        if (isMounted) {
          setIsSyncing(false);
          setLoading(false);
        }
      }
    }

    fetchContentAndTemplates();

    let syncInterval: NodeJS.Timeout | undefined;
    if (config.syncInterval && isOnline) {
      syncInterval = setInterval(fetchContentAndTemplates, config.syncInterval);
    }

    return () => {
      isMounted = false;
      if (syncInterval) {
        clearInterval(syncInterval);
      }
    };
  }, [config.projectId, config.syncInterval, config.offlineSupport, config.cacheMaxAge, isOnline]);

  return { content, loading, error, isSyncing, isOnline };
}

export function useProjectContent(config: KioskConfig, projectId: string) {
  const [content, setContent] = useState<Content[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let isMounted = true;

    async function fetchContent() {
      if (!isOnline && config.offlineSupport) {
        const cached = getFromCache(`${config.projectId}_project_${projectId}`);
        if (cached) {
          setContent(cached.content);
          setLoading(false);
          return;
        }
      }

      try {
        const data = await client.getContentByProject(projectId);
        if (isMounted) {
          const filteredContent = data.filter(item =>
            filterPublishedProjectContent([item]).length > 0 &&
            item.projectIds?.includes(projectId)
          );
          setContent(filteredContent);
          if (config.offlineSupport) {
            saveToCache(`${config.projectId}_project_${projectId}`, filteredContent);
          }
        }
      } catch (err) {
        if (isMounted && config.offlineSupport) {
          const cached = getFromCache(`${config.projectId}_project_${projectId}`);
          if (cached) {
            setContent(cached.content);
          } else {
            setError(err instanceof Error ? err : new Error('Failed to fetch project content'));
          }
        } else if (isMounted) {
          setError(err instanceof Error ? err : new Error('Failed to fetch project content'));
        }
      } finally {
        if (isMounted) {
          setLoading(false);
        }
      }
    }

    if (!isOnline && config.offlineSupport) {
      const cached = getFromCache(`${config.projectId}_project_${projectId}`);
      if (cached) {
        setContent(cached.content);
        setLoading(false);
      }
    }

    if (isOnline) {
      fetchContent();
    }

    return () => {
      isMounted = false;
    };
  }, [config.projectId, projectId, config.offlineSupport, isOnline]);

  return { content, loading, error, isOnline };
}

export function useContentWithTemplate(config: KioskConfig, contentId: string) {
  const [content, setContent] = useState<Content | null>(null);
  const [template, setTemplate] = useState<Template | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let isMounted = true;

    async function fetchContent() {
      if (!isOnline && config.offlineSupport) {
        //  Add offline cache handling here if needed.  This hook doesn't currently use caching.
      }
      try {
        const { content: contentData, template: templateData } = await client.getContentWithTemplate(contentId);
        setContent(contentData);
        setTemplate(templateData);
      } catch (err) {
        setError(err instanceof Error ? err : new Error('Failed to fetch content with template'));
      } finally {
        setLoading(false);
      }
    }

    fetchContent();
  }, [config.projectId, contentId, isOnline]);

  return { content, template, loading, error, isOnline };
}

export function useTemplates(config: KioskConfig) {
  const [templates, setTemplates] = useState<Template[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let isMounted = true;

    async function fetchTemplates() {
      if (!isOnline && config.offlineSupport) {
        // Add offline cache handling here if needed. This hook doesn't currently use caching.
      }
      try {
        const data = await client.getTemplates();
        setTemplates(data);
      } catch (err) {
        setError(err instanceof Error ? err : new Error('Failed to fetch templates'));
      } finally {
        setLoading(false);
      }
    }

    fetchTemplates();
  }, [config.projectId, isOnline]);

  return { templates, loading, error, isOnline };
}

export function useTemplate(config: KioskConfig, templateId: string) {
  const [template, setTemplate] = useState<Template | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let isMounted = true;

    async function fetchTemplate() {
      if (!isOnline && config.offlineSupport) {
        // Add offline cache handling here if needed. This hook doesn't currently use caching.
      }
      try {
        const data = await client.getTemplateById(templateId);
        setTemplate(data);
      } catch (err) {
        setError(err instanceof Error ? err : new Error('Failed to fetch template'));
      } finally {
        setLoading(false);
      }
    }

    fetchTemplate();
  }, [config.projectId, templateId, isOnline]);

  return { template, loading, error, isOnline };
}

export function useTemplateContent(config: KioskConfig, templateId: string) {
  const [content, setContent] = useState<Content[]>([]);
  const [template, setTemplate] = useState<Template | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let isMounted = true;

    async function fetchTemplateContent() {
      if (!isOnline && config.offlineSupport) {
        // Add offline cache handling here if needed. This hook doesn't currently use caching.
      }
      try {
        const templateData = await client.getTemplateById(templateId);
        setTemplate(templateData);

        if (templateData) {
          const publishedContent = await client.getPublishedContent();
          const templateContent = publishedContent.filter(c => c.templateId === templateId);
          setContent(templateContent);
        }
      } catch (err) {
        setError(err instanceof Error ? err : new Error('Failed to fetch template content'));
      } finally {
        setLoading(false);
      }
    }

    fetchTemplateContent();
  }, [config.projectId, templateId, isOnline]);

  return { content, template, loading, error, isOnline };
}

export function useOfflineContent(config: KioskConfig) {
  const [content, setContent] = useState<Content[]>([]);
  const [syncStatus, setSyncStatus] = useState<"synced" | "pending" | "failed">("pending");
  const [lastSynced, setLastSynced] = useState<number | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  useEffect(() => {
    const client = new KioskClient(config);
    let syncInterval: NodeJS.Timeout;

    async function syncContent() {
      if (!isOnline) return;

      setSyncStatus("pending");
      try {
        const data = await client.getPublishedContent();
        saveToCache(config.projectId, data);
        setContent(data);
        setLastSynced(Date.now());
        setSyncStatus("synced");
      } catch (err) {
        setSyncStatus("failed");
        setError(err instanceof Error ? err : new Error('Content sync failed'));
      }
    }

    syncContent();

    if (config.syncInterval) {
      syncInterval = setInterval(syncContent, config.syncInterval);
    }

    return () => {
      if (syncInterval) {
        clearInterval(syncInterval);
      }
    };
  }, [config.projectId, config.syncInterval, isOnline]);

  return { content, syncStatus, lastSynced, error, isOnline };
}

export function useContentSync(config: KioskConfig) {
  const [syncStatus, setSyncStatus] = useState<"idle" | "syncing" | "success" | "error">("idle");
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState<Error | null>(null);
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }

    function handleOffline() {
      setIsOnline(false);
    }

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  const sync = async (options?: SyncOptions) => {
    if (!isOnline) {
      setError(new Error("Cannot sync while offline"));
      return;
    }
    setSyncStatus("syncing");
    setProgress(0);
    setError(null);

    try {
      const client = new KioskClient(config);

      const progressInterval = setInterval(() => {
        setProgress(prev => Math.min(prev + 10, 90));
      }, 500);

      await client.getPublishedContent();

      clearInterval(progressInterval);
      setProgress(100);
      setSyncStatus("success");
    } catch (err) {
      setSyncStatus("error");
      setError(err instanceof Error ? err : new Error('Sync failed'));

      if (options?.onError) {
        options.onError(err instanceof Error ? err : new Error('Sync failed'));
      }
    }
  };

  return { sync, syncStatus, progress, error, isOnline };
}