/**
 * Export Context - Provides persistent state for export and filter operations.
 *
 * This context lifts state above the Tabs component so it persists
 * when users switch between tabs.
 *
 * @package GetMD
 * @since   1.0.0
 */

import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react';
import type { ReactNode } from 'react';
import type { DateRange } from 'react-day-picker';
import { format } from 'date-fns';
import type { ExportJob, FilterCriteria } from '../types/wordpress';
import { getAPI } from '../lib/api';

/**
 * Applied filter interface - represents a user-configured filter.
 */
export interface AppliedFilter {
  id: string;
  type: 'post_type' | 'author' | 'category' | 'tag' | 'status' | 'date_range';
  condition: 'include' | 'exclude' | 'within' | 'before' | 'after';
  values: string[] | number[] | { from: Date; to: Date };
  label: string;
}

/**
 * Media settings interface.
 */
export interface MediaSettingsValues {
  enable_media_export: boolean;
  include_featured_images: boolean;
  include_inline_images: boolean;
  include_galleries: boolean;
  include_attachments: boolean;
  download_external_images: boolean;
  optimize_images: boolean;
  image_size: 'thumbnail' | 'medium' | 'medium_large' | 'large' | 'full';
  max_width: number;
  max_height: number;
  quality: number;
}

/**
 * Context value interface defining all state and actions.
 */
interface ExportContextValue {
  // Export state
  isExporting: boolean;
  exportJob: ExportJob | null;
  error: string | null;

  // Preview state
  previewCount: number | null;
  isLoadingPreview: boolean;

  // Filter state
  appliedFilters: AppliedFilter[];
  filterType: string;
  filterCondition: string;
  filterValues: any[];
  dateRange: DateRange | undefined;
  fileStructure: 'single' | 'multiple';
  mediaSettings: MediaSettingsValues;

  // Export actions
  startExport: (filters: FilterCriteria) => Promise<void>;
  resetExport: () => void;

  // Filter setters
  setAppliedFilters: React.Dispatch<React.SetStateAction<AppliedFilter[]>>;
  setFilterType: React.Dispatch<React.SetStateAction<string>>;
  setFilterCondition: React.Dispatch<React.SetStateAction<string>>;
  setFilterValues: React.Dispatch<React.SetStateAction<any[]>>;
  setDateRange: React.Dispatch<React.SetStateAction<DateRange | undefined>>;
  setFileStructure: React.Dispatch<React.SetStateAction<'single' | 'multiple'>>;
  setMediaSettings: React.Dispatch<React.SetStateAction<MediaSettingsValues>>;
}

const ExportContext = createContext<ExportContextValue | undefined>(undefined);

interface ExportProviderProps {
  children: ReactNode;
}

/**
 * Export Provider component that manages export and filter state.
 */
export function ExportProvider({ children }: ExportProviderProps) {
  // Export state
  const [isExporting, setIsExporting] = useState(false);
  const [exportJob, setExportJob] = useState<ExportJob | null>(null);
  const [error, setError] = useState<string | null>(null);

  // Filter state
  const [appliedFilters, setAppliedFilters] = useState<AppliedFilter[]>([]);
  const [filterType, setFilterType] = useState<string>('post_type');
  const [filterCondition, setFilterCondition] = useState<string>('include');
  const [filterValues, setFilterValues] = useState<any[]>([]);
  const [dateRange, setDateRange] = useState<DateRange | undefined>();
  const [fileStructure, setFileStructure] = useState<'single' | 'multiple'>('multiple');

  // Media settings state (v0.5.0)
  const [mediaSettings, setMediaSettings] = useState<MediaSettingsValues>({
    enable_media_export: true,
    include_featured_images: true,
    include_inline_images: true,
    include_galleries: true,
    include_attachments: false,
    download_external_images: false,
    optimize_images: false,
    image_size: 'full',
    max_width: 2048,
    max_height: 2048,
    quality: 85,
  });

  // Preview state
  const [previewCount, setPreviewCount] = useState<number | null>(null);
  const [isLoadingPreview, setIsLoadingPreview] = useState(false);

  // Polling timeout ref - persists across renders and tab switches
  const pollingTimeoutRef = useRef<number | null>(null);
  const previewTimeoutRef = useRef<number | null>(null);

  // Cleanup polling and preview on unmount
  useEffect(() => {
    return () => {
      if (pollingTimeoutRef.current) {
        clearTimeout(pollingTimeoutRef.current);
      }
      if (previewTimeoutRef.current) {
        clearTimeout(previewTimeoutRef.current);
      }
    };
  }, []);

  /**
   * Build FilterCriteria from applied filters for API requests.
   */
  const buildFilterCriteria = useMemo((): FilterCriteria => {
    const criteria: FilterCriteria = {
      post_type: [],
      post_status: [],
      file_structure: fileStructure,
    };

    appliedFilters.forEach((filter) => {
      switch (filter.type) {
        case 'post_type':
          if (Array.isArray(filter.values)) {
            criteria.post_type = [...(criteria.post_type || []), ...filter.values] as string[];
          }
          break;
        case 'status':
          if (Array.isArray(filter.values)) {
            criteria.post_status = [...(criteria.post_status || []), ...filter.values] as string[];
          }
          break;
        case 'category':
          if (Array.isArray(filter.values)) {
            criteria.category = filter.values as number[];
            criteria.category_condition = filter.condition as 'include' | 'exclude';
          }
          break;
        case 'tag':
          if (Array.isArray(filter.values)) {
            criteria.tag = filter.values as number[];
            criteria.tag_condition = filter.condition as 'include' | 'exclude';
          }
          break;
        case 'author':
          if (Array.isArray(filter.values)) {
            criteria.author = filter.values as number[];
            criteria.author_condition = filter.condition as 'include' | 'exclude';
          }
          break;
        case 'date_range':
          if (typeof filter.values === 'object' && 'from' in filter.values && 'to' in filter.values) {
            criteria.date_range = {
              start: format(filter.values.from, 'yyyy-MM-dd'),
              end: format(filter.values.to, 'yyyy-MM-dd'),
            };
          }
          break;
      }
    });

    // Remove empty arrays to let backend apply defaults
    if (criteria.post_type && criteria.post_type.length === 0) {
      delete criteria.post_type;
    }
    if (criteria.post_status && criteria.post_status.length === 0) {
      delete criteria.post_status;
    }

    return criteria;
  }, [appliedFilters, fileStructure]);

  /**
   * Fetch preview count when filters change (debounced).
   */
  useEffect(() => {
    // Clear any pending preview request
    if (previewTimeoutRef.current) {
      clearTimeout(previewTimeoutRef.current);
    }

    const fetchPreview = async () => {
      setIsLoadingPreview(true);
      try {
        const result = await getAPI().getExportPreview(buildFilterCriteria);
        setPreviewCount(result.count);
      } catch (err) {
        console.error('Failed to fetch preview count:', err);
        setPreviewCount(null);
      } finally {
        setIsLoadingPreview(false);
      }
    };

    // Debounce: wait 300ms after last filter change before fetching
    previewTimeoutRef.current = window.setTimeout(fetchPreview, 300);

    return () => {
      if (previewTimeoutRef.current) {
        clearTimeout(previewTimeoutRef.current);
      }
    };
  }, [buildFilterCriteria]);

  /**
   * Poll export status until complete or failed.
   */
  const pollExportStatus = useCallback(async (exportId: number) => {
    try {
      const job = await getAPI().getExportStatus(exportId);
      setExportJob(job);

      if (job.status === 'processing' || job.status === 'pending') {
        pollingTimeoutRef.current = window.setTimeout(() => pollExportStatus(exportId), 2000);
      } else if (job.status === 'completed') {
        setIsExporting(false);
      } else if (job.status === 'failed') {
        setError(job.error || 'Export failed');
        setIsExporting(false);
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to check export status');
      setIsExporting(false);
    }
  }, []);

  /**
   * Start a new export job with the given filters.
   */
  const startExport = useCallback(async (filters: FilterCriteria) => {
    // Clear any existing polling
    if (pollingTimeoutRef.current) {
      clearTimeout(pollingTimeoutRef.current);
      pollingTimeoutRef.current = null;
    }

    setIsExporting(true);
    setError(null);
    setExportJob(null);

    try {
      const job = await getAPI().createExport(filters);
      setExportJob(job);
      pollExportStatus(job.export_id);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Export failed');
      setIsExporting(false);
    }
  }, [pollExportStatus]);

  /**
   * Reset export state (clear job, error, and stop polling).
   */
  const resetExport = useCallback(() => {
    if (pollingTimeoutRef.current) {
      clearTimeout(pollingTimeoutRef.current);
      pollingTimeoutRef.current = null;
    }
    setIsExporting(false);
    setExportJob(null);
    setError(null);
  }, []);

  const value: ExportContextValue = {
    // Export state
    isExporting,
    exportJob,
    error,

    // Preview state
    previewCount,
    isLoadingPreview,

    // Filter state
    appliedFilters,
    filterType,
    filterCondition,
    filterValues,
    dateRange,
    fileStructure,
    mediaSettings,

    // Export actions
    startExport,
    resetExport,

    // Filter setters
    setAppliedFilters,
    setFilterType,
    setFilterCondition,
    setFilterValues,
    setDateRange,
    setFileStructure,
    setMediaSettings,
  };

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

/**
 * Hook to access the export context.
 * Must be used within an ExportProvider.
 */
export function useExport(): ExportContextValue {
  const context = useContext(ExportContext);
  if (context === undefined) {
    throw new Error('useExport must be used within an ExportProvider');
  }
  return context;
}
