// JavaScript wrapper for githubJsonService (for use with node server.js)
import { writeFile, mkdir, readdir, readFile } from 'fs/promises';
import { join } from 'path';
import { repositoryService } from './repositoryService.js';

// Get environment variables
const getEnv = () => ({
  REPO_BRANCH: process.env.REPO_BRANCH || 'main',
  JSON_FOLDER: process.env.JSON_FOLDER || 'json',
  REPO_URL: process.env.REPO_URL || 'https://github.com/community-scripts/ProxmoxVE',
  GITHUB_TOKEN: process.env.GITHUB_TOKEN
});

class GitHubJsonService {
  constructor() {
    this.branch = null;
    this.jsonFolder = null;
    this.localJsonDirectory = null;
    this.scriptCache = new Map();
  }

  initializeConfig() {
    if (this.branch === null) {
      const env = getEnv();
      this.branch = env.REPO_BRANCH;
      this.jsonFolder = env.JSON_FOLDER;
      this.localJsonDirectory = join(process.cwd(), 'scripts', 'json');
    }
  }

  getBaseUrl(repoUrl) {
    const urlMatch = /github\.com\/([^\/]+)\/([^\/]+)/.exec(repoUrl);
    if (!urlMatch) {
      throw new Error(`Invalid GitHub repository URL: ${repoUrl}`);
    }
    
    const [, owner, repo] = urlMatch;
    return `https://api.github.com/repos/${owner}/${repo}`;
  }

  extractRepoPath(repoUrl) {
    const match = /github\.com\/([^\/]+)\/([^\/]+)/.exec(repoUrl);
    if (!match) {
      throw new Error('Invalid GitHub repository URL');
    }
    return `${match[1]}/${match[2]}`;
  }

  async fetchFromGitHub(repoUrl, endpoint) {
    const baseUrl = this.getBaseUrl(repoUrl);
    const env = getEnv();
    
    const headers = {
      'Accept': 'application/vnd.github.v3+json',
      'User-Agent': 'PVEScripts-Local/1.0',
    };
    
    if (env.GITHUB_TOKEN) {
      headers.Authorization = `token ${env.GITHUB_TOKEN}`;
    }
    
    const response = await fetch(`${baseUrl}${endpoint}`, { headers });

    if (!response.ok) {
      if (response.status === 403) {
        const error = new Error(`GitHub API rate limit exceeded. Consider setting GITHUB_TOKEN for higher limits. Status: ${response.status} ${response.statusText}`);
        error.name = 'RateLimitError';
        throw error;
      }
      throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
    }

    return response.json();
  }

  async downloadJsonFile(repoUrl, filePath) {
    this.initializeConfig();
    const repoPath = this.extractRepoPath(repoUrl);
    const rawUrl = `https://raw.githubusercontent.com/${repoPath}/${this.branch}/${filePath}`;
    const env = getEnv();
    
    const headers = {
      'User-Agent': 'PVEScripts-Local/1.0',
    };
    
    if (env.GITHUB_TOKEN) {
      headers.Authorization = `token ${env.GITHUB_TOKEN}`;
    }
    
    const response = await fetch(rawUrl, { headers });
    if (!response.ok) {
      if (response.status === 403) {
        const error = new Error(`GitHub rate limit exceeded while downloading ${filePath}. Consider setting GITHUB_TOKEN for higher limits.`);
        error.name = 'RateLimitError';
        throw error;
      }
      throw new Error(`Failed to download ${filePath}: ${response.status} ${response.statusText}`);
    }

    const content = await response.text();
    const script = JSON.parse(content);
    script.repository_url = repoUrl;
    return script;
  }

  async getJsonFiles(repoUrl) {
    this.initializeConfig();
    
    try {
      const files = await this.fetchFromGitHub(
        repoUrl,
        `/contents/${this.jsonFolder}?ref=${this.branch}`
      );
      
      return files.filter(file => file.name.endsWith('.json'));
    } catch (error) {
      console.error(`Error fetching JSON files from GitHub (${repoUrl}):`, error);
      throw new Error(`Failed to fetch script files from repository: ${repoUrl}`);
    }
  }

  async getAllScripts(repoUrl) {
    try {
      const jsonFiles = await this.getJsonFiles(repoUrl);
      const scripts = [];

      for (const file of jsonFiles) {
        try {
          const script = await this.downloadJsonFile(repoUrl, file.path);
          scripts.push(script);
        } catch (error) {
          console.error(`Failed to download script ${file.name} from ${repoUrl}:`, error);
        }
      }

      return scripts;
    } catch (error) {
      console.error(`Error fetching all scripts from ${repoUrl}:`, error);
      throw new Error(`Failed to fetch scripts from repository: ${repoUrl}`);
    }
  }

  async getScriptCards(repoUrl) {
    try {
      const scripts = await this.getAllScripts(repoUrl);
      
      return scripts.map(script => ({
        name: script.name,
        slug: script.slug,
        description: script.description,
        logo: script.logo,
        type: script.type,
        updateable: script.updateable,
        website: script.website,
        repository_url: script.repository_url,
      }));
    } catch (error) {
      console.error(`Error creating script cards from ${repoUrl}:`, error);
      throw new Error(`Failed to create script cards from repository: ${repoUrl}`);
    }
  }

  async getScriptBySlug(slug, repoUrl) {
    try {
      const localScript = await this.getScriptFromLocal(slug);
      if (localScript) {
        if (repoUrl && localScript.repository_url !== repoUrl) {
          return null;
        }
        return localScript;
      }

      if (repoUrl) {
        try {
          this.initializeConfig();
          const script = await this.downloadJsonFile(repoUrl, `${this.jsonFolder}/${slug}.json`);
          return script;
        } catch {
          return null;
        }
      }

      const enabledRepos = await repositoryService.getEnabledRepositories();
      for (const repo of enabledRepos) {
        try {
          this.initializeConfig();
          const script = await this.downloadJsonFile(repo.url, `${this.jsonFolder}/${slug}.json`);
          return script;
        } catch {
          // Continue to next repo
        }
      }

      return null;
    } catch (error) {
      console.error('Error fetching script by slug:', error);
      throw new Error(`Failed to fetch script: ${slug}`);
    }
  }

  async getScriptFromLocal(slug) {
    try {
      if (this.scriptCache.has(slug)) {
        return this.scriptCache.get(slug);
      }

      this.initializeConfig();
      const filePath = join(this.localJsonDirectory, `${slug}.json`);
      const content = await readFile(filePath, 'utf-8');
      const script = JSON.parse(content);
      
      if (!script.repository_url) {
        const env = getEnv();
        script.repository_url = env.REPO_URL;
      }
      
      this.scriptCache.set(slug, script);
      
      return script;
    } catch {
      return null;
    }
  }

  async syncJsonFilesForRepo(repoUrl) {
    try {
      console.log(`Starting JSON sync from repository: ${repoUrl}`);
      
      const githubFiles = await this.getJsonFiles(repoUrl);
      console.log(`Found ${githubFiles.length} JSON files in repository ${repoUrl}`);
      
      const localFiles = await this.getLocalJsonFiles();
      console.log(`Found ${localFiles.length} local JSON files`);
      
      const filesToSync = await this.findFilesToSyncForRepo(repoUrl, githubFiles, localFiles);
      console.log(`Found ${filesToSync.length} files that need syncing from ${repoUrl}`);
      
      if (filesToSync.length === 0) {
        return {
          success: true,
          message: `All JSON files are up to date for repository: ${repoUrl}`,
          count: 0,
          syncedFiles: []
        };
      }
      
      const syncedFiles = await this.syncSpecificFiles(repoUrl, filesToSync);
      
      return {
        success: true,
        message: `Successfully synced ${syncedFiles.length} JSON files from ${repoUrl}`,
        count: syncedFiles.length,
        syncedFiles
      };
    } catch (error) {
      console.error(`JSON sync failed for ${repoUrl}:`, error);
      return {
        success: false,
        message: `Failed to sync JSON files from ${repoUrl}: ${error instanceof Error ? error.message : 'Unknown error'}`,
        count: 0,
        syncedFiles: []
      };
    }
  }

  async syncJsonFiles() {
    try {
      console.log('Starting multi-repository JSON sync...');
      
      const enabledRepos = await repositoryService.getEnabledRepositories();
      
      if (enabledRepos.length === 0) {
        return {
          success: false,
          message: 'No enabled repositories found',
          count: 0,
          syncedFiles: []
        };
      }

      console.log(`Found ${enabledRepos.length} enabled repositories`);
      
      const allSyncedFiles = [];
      const processedSlugs = new Set();
      let totalSynced = 0;

      for (const repo of enabledRepos) {
        try {
          console.log(`Syncing from repository: ${repo.url} (priority: ${repo.priority})`);
          
          const result = await this.syncJsonFilesForRepo(repo.url);
          
          if (result.success) {
            const newFiles = result.syncedFiles.filter(file => {
              const slug = file.replace('.json', '');
              if (processedSlugs.has(slug)) {
                return false;
              }
              processedSlugs.add(slug);
              return true;
            });
            
            allSyncedFiles.push(...newFiles);
            totalSynced += newFiles.length;
          } else {
            console.error(`Failed to sync from ${repo.url}: ${result.message}`);
          }
        } catch (error) {
          console.error(`Error syncing from ${repo.url}:`, error);
        }
      }

      await this.updateExistingFilesWithRepositoryUrl();

      return {
        success: true,
        message: `Successfully synced ${totalSynced} JSON files from ${enabledRepos.length} repositories`,
        count: totalSynced,
        syncedFiles: allSyncedFiles
      };
    } catch (error) {
      console.error('Multi-repository JSON sync failed:', error);
      return {
        success: false,
        message: `Failed to sync JSON files: ${error instanceof Error ? error.message : 'Unknown error'}`,
        count: 0,
        syncedFiles: []
      };
    }
  }

  async updateExistingFilesWithRepositoryUrl() {
    try {
      this.initializeConfig();
      const files = await this.getLocalJsonFiles();
      const env = getEnv();
      const mainRepoUrl = env.REPO_URL;
      
      for (const file of files) {
        try {
          const filePath = join(this.localJsonDirectory, file);
          const content = await readFile(filePath, 'utf-8');
          const script = JSON.parse(content);
          
          if (!script.repository_url) {
            script.repository_url = mainRepoUrl;
            await writeFile(filePath, JSON.stringify(script, null, 2), 'utf-8');
            console.log(`Updated ${file} with repository_url: ${mainRepoUrl}`);
          }
        } catch (error) {
          console.error(`Error updating ${file}:`, error);
        }
      }
    } catch (error) {
      console.error('Error updating existing files with repository_url:', error);
    }
  }

  async getLocalJsonFiles() {
    this.initializeConfig();
    try {
      const files = await readdir(this.localJsonDirectory);
      return files.filter(f => f.endsWith('.json'));
    } catch {
      return [];
    }
  }

  async findFilesToSyncForRepo(repoUrl, githubFiles, localFiles) {
    const filesToSync = [];
    
    for (const ghFile of githubFiles) {
      const localFilePath = join(this.localJsonDirectory, ghFile.name);
      
      let needsSync = false;
      
      if (!localFiles.includes(ghFile.name)) {
        needsSync = true;
      } else {
        try {
          const content = await readFile(localFilePath, 'utf-8');
          const script = JSON.parse(content);
          
          if (!script.repository_url || script.repository_url !== repoUrl) {
            needsSync = true;
          }
        } catch {
          needsSync = true;
        }
      }
      
      if (needsSync) {
        filesToSync.push(ghFile);
      }
    }
    
    return filesToSync;
  }

  async syncSpecificFiles(repoUrl, filesToSync) {
    this.initializeConfig();
    const syncedFiles = [];
    
    await mkdir(this.localJsonDirectory, { recursive: true });
    
    for (const file of filesToSync) {
      try {
        const script = await this.downloadJsonFile(repoUrl, file.path);
        const filename = `${script.slug}.json`;
        const filePath = join(this.localJsonDirectory, filename);
        
        script.repository_url = repoUrl;
        
        await writeFile(filePath, JSON.stringify(script, null, 2), 'utf-8');
        syncedFiles.push(filename);
        
        this.scriptCache.delete(script.slug);
      } catch (error) {
        console.error(`Failed to sync ${file.name} from ${repoUrl}:`, error);
      }
    }
    
    return syncedFiles;
  }
}

// Singleton instance
export const githubJsonService = new GitHubJsonService();
