import React, { createContext, useContext, useEffect, useState, ReactNode, useMemo } from 'react';
import { map } from 'rxjs/operators';
import { database, collections } from '../database';
import { runSyncGuarded, startAutoSync, stopAutoSync } from '../database/sync';
import { getAuthToken } from '../services/auth';
import { DEFAULT_CATEGORIES } from '../constants';
import Category from '../models/Category';
import RepeatProfile from '../models/RepeatProfile';
import { Q } from '@nozbe/watermelondb';

interface DatabaseContextType {
  database: typeof database;
  collections: typeof collections;
  isReady: boolean;
  initializeDatabase: () => Promise<void>;
}

const DatabaseContext = createContext<DatabaseContextType | null>(null);

export function DatabaseProvider({ children }: { children: ReactNode }) {
  const [isReady, setIsReady] = useState(false);

  const initializeDatabase = async () => {
    try {
      const existingCategories = await collections.categories.query().fetch();
      
      // Merge categories with the same spelling (case-insensitive)
      const categoriesByName = new Map<string, typeof existingCategories[0]>();
      const duplicates: typeof existingCategories = [];
      
      for (const cat of existingCategories) {
        const key = cat.name.toLowerCase();
        if (categoriesByName.has(key)) {
          duplicates.push(cat);
        } else {
          categoriesByName.set(key, cat);
        }
      }
      
      if (duplicates.length > 0) {
        await database.write(async () => {
          for (const dup of duplicates) {
            const keep = categoriesByName.get(dup.name.toLowerCase())!;
            // Reassign tasks from duplicate to kept category
            const tasksToUpdate = await collections.tasks.query(Q.where('category_id', dup.id)).fetch();
            for (const task of tasksToUpdate) {
              await task.update((t) => { t.categoryId = keep.id; });
            }
            // Reassign subtasks too (they're linked via task_id, so we update their parent tasks)
            const subtasksToUpdate = await collections.subtasks.query(Q.where('task_id', dup.id)).fetch();
            for (const sub of subtasksToUpdate) {
              const parentTask = await collections.tasks.find(sub.taskId);
              await parentTask.update((t) => { t.categoryId = keep.id; });
            }
            // Delete duplicate category
            await dup.destroyPermanently();
          }
        });
      }
      
      const freshCategories = await collections.categories.query().fetch();
      
      if (freshCategories.length === 0) {
        await database.write(async () => {
          for (const cat of DEFAULT_CATEGORIES) {
            await collections.categories.create((c) => {
              c.name = cat.name;
              c.color = cat.color;
              c.order = cat.order;
              c.createdAt = new Date();
              c.updatedAt = new Date();
            });
          }
        });
      }
      
      setIsReady(true);

      const token = await getAuthToken();
      if (token) {
        runSyncGuarded().catch(() => {});
        startAutoSync();
      } else {
        stopAutoSync();
      }
    } catch (error) {
      console.error('Failed to initialize database:', error);
      setIsReady(true);
    }
  };

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    initializeDatabase();
    return () => {
      stopAutoSync();
    };
  }, []);

  return (
    <DatabaseContext.Provider value={{ database, collections, isReady, initializeDatabase }}>
      {children}
    </DatabaseContext.Provider>
  );
}

export function useDatabase() {
  const context = useContext(DatabaseContext);
  if (!context) {
    throw new Error('useDatabase must be used within a DatabaseProvider');
  }
  return context;
}

export function useCategories(): Category[] {
  const { collections } = useDatabase();
  const [categories, setCategories] = useState<Category[]>([]);

  useEffect(() => {
    let mounted = true;
    const subscription = collections.categories
      .query()
      .observeWithColumns(['name', 'color', 'order'])
      .pipe(map((cats) => [...cats].sort((a, b) => a.order - b.order)))
      .subscribe({
        next: (cats) => {
          if (mounted) setCategories(cats);
        },
        error: () => {},
      });

    return () => {
      mounted = false;
      subscription.unsubscribe();
    };
  }, [collections]);

  return categories;
}

export function useUniqueCategories(): Category[] {
  const categories = useCategories();
  return useMemo(() => {
    const seen = new Map<string, Category>();
    for (const cat of categories) {
      if (!seen.has(cat.name)) {
        seen.set(cat.name, cat);
      }
    }
    return Array.from(seen.values()).sort((a, b) => a.order - b.order);
  }, [categories]);
}

export function useRepeatProfiles(): RepeatProfile[] {
  const { collections } = useDatabase();
  const [profiles, setProfiles] = useState<RepeatProfile[]>([]);

  useEffect(() => {
    let mounted = true;
    const subscription = collections.repeatProfiles
      .query()
      .observeWithColumns(['name', 'repeat', 'repeat_interval', 'repeat_days'])
      .pipe(map((rows) => [...rows].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())))
      .subscribe({
        next: (rows) => {
          if (mounted) setProfiles(rows);
        },
        error: () => {},
      });

    return () => {
      mounted = false;
      subscription.unsubscribe();
    };
  }, [collections]);

  return profiles;
}