 
import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
import { scriptManager } from "~/server/lib/scripts";
import { githubJsonService } from "~/server/services/githubJsonService";
import { localScriptsService } from "~/server/services/localScripts";
import { scriptDownloaderService } from "~/server/services/scriptDownloader.js";
import { AutoSyncService } from "~/server/services/autoSyncService";
import { repositoryService } from "~/server/services/repositoryService";
import type { ScriptCard } from "~/types/script";

export const scriptsRouter = createTRPCRouter({
  // Get all available scripts
  getScripts: publicProcedure
    .query(async () => {
      const scripts = await scriptManager.getScripts();
      return {
        scripts,
        directoryInfo: scriptManager.getScriptsDirectoryInfo()
      };
    }),

  // Get CT scripts (for local scripts tab)
  getCtScripts: publicProcedure
    .query(async () => {
      const scripts = await scriptManager.getCtScripts();
      return {
        scripts,
        directoryInfo: scriptManager.getScriptsDirectoryInfo()
      };
    }),

  // Get all downloaded scripts from all directories
  getAllDownloadedScripts: publicProcedure
    .query(async () => {
      const scripts = await scriptManager.getAllDownloadedScripts();
      return {
        scripts,
        directoryInfo: scriptManager.getScriptsDirectoryInfo()
      };
    }),

 
  // Get script content for viewing
  getScriptContent: publicProcedure
    .input(z.object({ path: z.string() }))
    .query(async ({ input }) => {
      try {
        const { readFile } = await import('fs/promises');
        const { join } = await import('path');
        const { env } = await import('~/env');
        
        const scriptsDir = join(process.cwd(), env.SCRIPTS_DIRECTORY);
        const fullPath = join(scriptsDir, input.path);
        
        // Security check: ensure the path is within the scripts directory
        if (!fullPath.startsWith(scriptsDir)) {
          throw new Error('Invalid script path');
        }
        
        const content = await readFile(fullPath, 'utf-8');
        return { success: true, content };
      } catch (error) {
        console.error('Error reading script content:', error);
        return { success: false, error: 'Failed to read script content' };
      }
    }),

  // Validate script path
  validateScript: publicProcedure
    .input(z.object({ scriptPath: z.string() }))
    .query(async ({ input }) => {
      const validation = scriptManager.validateScriptPath(input.scriptPath);
      return validation;
    }),

  // Get directory information
  getDirectoryInfo: publicProcedure
    .query(async () => {
      return scriptManager.getScriptsDirectoryInfo();
    }),

  // Local script routes (using scripts/json directory)
  // Get all script cards from local directory
  getScriptCards: publicProcedure
    .query(async () => {
      try {
        const cards = await localScriptsService.getScriptCards();
        return { success: true, cards };
      } catch (error) {
        console.error('Error in getScriptCards:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to fetch script cards',
          cards: []
        };
      }
    }),

  // Get all scripts from GitHub (1 API call + raw downloads)
  getAllScripts: publicProcedure
    .query(async () => {
      try {
        const scripts = await localScriptsService.getAllScripts();
        return { success: true, scripts };
      } catch (error) {
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to fetch scripts',
          scripts: []
        };
      }
    }),

  // Get script by slug from GitHub (1 API call + raw downloads)
  getScriptBySlug: publicProcedure
    .input(z.object({ slug: z.string() }))
    .query(async ({ input }) => {
      try {
        console.log('getScriptBySlug called with slug:', input.slug);
        console.log('githubJsonService methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(githubJsonService)));
        console.log('githubJsonService.getScriptBySlug type:', typeof githubJsonService.getScriptBySlug);
        
        if (typeof githubJsonService.getScriptBySlug !== 'function') {
          return {
            success: false,
            error: 'getScriptBySlug method is not available on githubJsonService',
            script: null
          };
        }
        
        const script = await githubJsonService.getScriptBySlug(input.slug);
        if (!script) {
          return {
            success: false,
            error: 'Script not found',
            script: null
          };
        }
        return { success: true, script };
      } catch (error) {
        console.error('Error in getScriptBySlug:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to fetch script',
          script: null
        };
      }
    }),

  // Get metadata (categories and other metadata)
  getMetadata: publicProcedure
    .query(async () => {
      try {
        const metadata = await localScriptsService.getMetadata();
        return { success: true, metadata };
      } catch (error) {
        console.error('Error in getMetadata:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to fetch metadata',
          metadata: null
        };
      }
    }),

  // Get script cards with category information
  getScriptCardsWithCategories: publicProcedure
    .query(async () => {
      try {
        const [cards, metadata, enabledRepos] = await Promise.all([
          localScriptsService.getScriptCards(),
          localScriptsService.getMetadata(),
          repositoryService.getEnabledRepositories()
        ]);

        // Get all scripts to access their categories
        const scripts = await localScriptsService.getAllScripts();
        
        // Create a set of enabled repository URLs for fast lookup
        const enabledRepoUrls = new Set(enabledRepos.map((repo: { url: string }) => repo.url));
        
        // Create category ID to name mapping
        const categoryMap: Record<number, string> = {};
        if (metadata?.categories) {
          metadata.categories.forEach((cat: any) => {
            categoryMap[cat.id] = cat.name;
          });
        }

        // Enhance cards with category information and additional script data
        const cardsWithCategories = cards.map((card: ScriptCard) => {
          const script = scripts.find(s => s.slug === card.slug);
          const categoryNames: string[] = script?.categories?.map(id => categoryMap[id]).filter((name): name is string => typeof name === 'string') ?? [];
          
          // Extract OS and version from first install method
          const firstInstallMethod = script?.install_methods?.[0];
          const os = firstInstallMethod?.resources?.os;
          const version = firstInstallMethod?.resources?.version;
          // Extract install basenames for robust local matching (e.g., execute.sh -> execute)
          const install_basenames = (script?.install_methods ?? [])
            .map(m => m?.script)
            .filter((p): p is string => typeof p === 'string')
            .map(p => {
              const parts = p.split('/');
              const file = parts[parts.length - 1] ?? '';
              return file.replace(/\.(sh|bash|py|js|ts)$/i, '');
            });
          
          return {
            ...card,
            categories: script?.categories ?? [],
            categoryNames: categoryNames,
            // Add date_created from script
            date_created: script?.date_created,
            // Add OS and version from install methods
            os: os,
            version: version,
            // Add interface port
            interface_port: script?.interface_port,
            install_basenames,
            // Add repository_url from script
            repository_url: script?.repository_url ?? card.repository_url,
          } as ScriptCard;
        });

        // Filter cards to only include scripts from enabled repositories
        // For backward compatibility, include scripts without repository_url
        const filteredCards = cardsWithCategories.filter((card: ScriptCard) => {
          const repoUrl = card.repository_url;
          
          // If script has no repository_url, include it for backward compatibility
          if (!repoUrl) {
            return true;
          }
          
          // Only include scripts from enabled repositories
          return enabledRepoUrls.has(repoUrl);
        });

        return { success: true, cards: filteredCards, metadata };
      } catch (error) {
        console.error('Error in getScriptCardsWithCategories:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to fetch script cards with categories',
          cards: [],
          metadata: null
        };
      }
    }),

  // Resync scripts from GitHub (1 API call + raw downloads)
  resyncScripts: publicProcedure
    .mutation(async () => {
      try {
        // Sync JSON files using 1 API call + raw downloads
        const result = await githubJsonService.syncJsonFiles();
        
        return { 
          success: result.success, 
          message: result.message,
          count: result.count
        };
      } catch (error) {
        console.error('Error in resyncScripts:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to resync scripts. Make sure REPO_URL is set.',
          count: 0
        };
      }
    }),

  // Load script files from GitHub
  loadScript: publicProcedure
    .input(z.object({ slug: z.string() }))
    .mutation(async ({ input }) => {
      try {
        // Get the script details
        const script = await localScriptsService.getScriptBySlug(input.slug);
        if (!script) {
          return {
            success: false,
            error: 'Script not found',
            files: []
          };
        }

        // Load the script files
        const result = await scriptDownloaderService.loadScript(script);
        return result;
      } catch (error) {
        console.error('Error in loadScript:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to load script',
          files: []
        };
      }
    }),

  // Load multiple scripts from GitHub
  loadMultipleScripts: publicProcedure
    .input(z.object({ slugs: z.array(z.string()) }))
    .mutation(async ({ input }) => {
      try {
        const successful = [];
        const failed = [];

        for (const slug of input.slugs) {
          try {
            // Get the script details
            const script = await localScriptsService.getScriptBySlug(slug);
            if (!script) {
              failed.push({ slug, error: 'Script not found' });
              continue;
            }

            // Load the script files
            const result = await scriptDownloaderService.loadScript(script);
            if (result.success) {
              successful.push({ slug, files: result.files });
            } else {
              const error = 'error' in result ? result.error : 'Failed to load script';
              failed.push({ slug, error });
            }
          } catch (error) {
            failed.push({ 
              slug, 
              error: error instanceof Error ? error.message : 'Failed to load script' 
            });
          }
        }

        return {
          success: true,
          message: `Downloaded ${successful.length} scripts successfully, ${failed.length} failed`,
          successful,
          failed,
          total: input.slugs.length
        };
      } catch (error) {
        console.error('Error in loadMultipleScripts:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to load multiple scripts',
          successful: [],
          failed: [],
          total: 0
        };
      }
    }),

  // Check if script files exist locally
  checkScriptFiles: publicProcedure
    .input(z.object({ slug: z.string() }))
    .query(async ({ input }) => {
      try {
        const script = await localScriptsService.getScriptBySlug(input.slug);
        if (!script) {
          return {
            success: false,
            error: 'Script not found',
            ctExists: false,
            installExists: false,
            files: []
          };
        }

        const result = await scriptDownloaderService.checkScriptExists(script);
        return {
          success: true,
          ...result
        };
      } catch (error) {
        console.error('Error in checkScriptFiles:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to check script files',
          ctExists: false,
          installExists: false,
          files: []
        };
      }
    }),

  // Delete script files
  deleteScript: publicProcedure
    .input(z.object({ slug: z.string() }))
    .mutation(async ({ input }) => {
      try {
        // Get the script details
        const script = await localScriptsService.getScriptBySlug(input.slug);
        if (!script) {
          return {
            success: false,
            error: 'Script not found',
            deletedFiles: []
          };
        }

        // Delete the script files
        const result = await scriptDownloaderService.deleteScript(script);
        return result;
      } catch (error) {
        console.error('Error in deleteScript:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to delete script',
          deletedFiles: []
        };
      }
    }),

  // Compare local and remote script content
  compareScriptContent: publicProcedure
    .input(z.object({ slug: z.string() }))
    .query(async ({ input }) => {
      try {
        const script = await localScriptsService.getScriptBySlug(input.slug);
        if (!script) {
          return {
            success: false,
            error: 'Script not found',
            hasDifferences: false,
            differences: []
          };
        }

        const result = await scriptDownloaderService.compareScriptContent(script);
        return {
          success: true,
          ...result
        };
      } catch (error) {
        console.error('Error in compareScriptContent:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to compare script content',
          hasDifferences: false,
          differences: []
        };
      }
    }),

  // Get diff content for a specific script file
  getScriptDiff: publicProcedure
    .input(z.object({ slug: z.string(), filePath: z.string() }))
    .query(async ({ input }) => {
      try {
        const script = await localScriptsService.getScriptBySlug(input.slug);
        if (!script) {
          return {
            success: false,
            error: 'Script not found',
            diff: null
          };
        }

        const result = await scriptDownloaderService.getScriptDiff(script, input.filePath);
        return {
          success: true,
          ...result
        };
      } catch (error) {
        console.error('Error in getScriptDiff:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to get script diff',
          diff: null
        };
      }
    }),

  // Check if running on Proxmox VE host
  checkProxmoxVE: publicProcedure
    .query(async () => {
      try {
        const { spawn } = await import('child_process');
        
        return new Promise((resolve) => {
          const child = spawn('command', ['-v', 'pveversion'], {
            stdio: ['pipe', 'pipe', 'pipe'],
            shell: true
          });


          child.on('close', (code) => {
            // If command exits with code 0, pveversion command exists
            if (code === 0) {
              resolve({
                success: true,
                isProxmoxVE: true,
                message: 'Running on Proxmox VE host'
              });
            } else {
              resolve({
                success: true,
                isProxmoxVE: false,
                message: 'Not running on Proxmox VE host'
              });
            }
          });

          child.on('error', (error) => {
            resolve({
              success: false,
              isProxmoxVE: false,
              error: error.message,
              message: 'Failed to check Proxmox VE status'
            });
          });
        });
      } catch (error) {
        console.error('Error in checkProxmoxVE:', error);
        return {
          success: false,
          isProxmoxVE: false,
          error: error instanceof Error ? error.message : 'Failed to check Proxmox VE status',
          message: 'Failed to check Proxmox VE status'
        };
      }
    }),

  // Auto-sync settings and operations
  getAutoSyncSettings: publicProcedure
    .query(async () => {
      try {
        const autoSyncService = new AutoSyncService();
        const settings = autoSyncService.loadSettings();
        return { success: true, settings };
      } catch (error) {
        console.error('Error getting auto-sync settings:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to get auto-sync settings',
          settings: null
        };
      }
    }),

  saveAutoSyncSettings: publicProcedure
    .input(z.object({
      autoSyncEnabled: z.boolean(),
      syncIntervalType: z.enum(['predefined', 'custom']),
      syncIntervalPredefined: z.string().optional(),
      syncIntervalCron: z.string().optional(),
      autoDownloadNew: z.boolean(),
      autoUpdateExisting: z.boolean(),
      notificationEnabled: z.boolean(),
      appriseUrls: z.array(z.string()).optional()
    }))
    .mutation(async ({ input }) => {
      try {
        // Use the global auto-sync service instance
        const { getAutoSyncService, setAutoSyncService } = await import('~/server/lib/autoSyncInit');
        let autoSyncService = getAutoSyncService();
        
        // If no global instance exists, create one
        if (!autoSyncService) {
          const { AutoSyncService } = await import('~/server/services/autoSyncService');
          autoSyncService = new AutoSyncService();
          setAutoSyncService(autoSyncService);
        }
        
        // Save settings to both .env file and service instance
        autoSyncService.saveSettings(input);
        
        // Reschedule auto-sync if enabled
        if (input.autoSyncEnabled) {
          autoSyncService.scheduleAutoSync();
          console.log('Auto-sync rescheduled with new settings');
        } else {
          autoSyncService.stopAutoSync();
          // Ensure the service is completely stopped and won't restart
          autoSyncService.isRunning = false;
          console.log('Auto-sync stopped');
        }
        
        return { success: true, message: 'Auto-sync settings saved successfully' };
      } catch (error) {
        console.error('Error saving auto-sync settings:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to save auto-sync settings'
        };
      }
    }),

  testNotification: publicProcedure
    .mutation(async () => {
      try {
        const autoSyncService = new AutoSyncService();
        const result = await autoSyncService.testNotification();
        return result;
      } catch (error) {
        console.error('Error testing notification:', error);
        return {
          success: false,
          message: error instanceof Error ? error.message : 'Failed to test notification'
        };
      }
    }),

  triggerManualAutoSync: publicProcedure
    .mutation(async () => {
      try {
        const autoSyncService = new AutoSyncService();
        const result = await autoSyncService.executeAutoSync();
        return {
          success: true,
          message: 'Manual auto-sync completed successfully',
          result
        };
      } catch (error) {
        console.error('Error in manual auto-sync:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to execute manual auto-sync',
          result: null
        };
      }
    }),

  getAutoSyncStatus: publicProcedure
    .query(async () => {
      try {
        const autoSyncService = new AutoSyncService();
        const status = autoSyncService.getStatus();
        return { success: true, status };
      } catch (error) {
        console.error('Error getting auto-sync status:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to get auto-sync status',
          status: null
        };
      }
    })
});
