import { BrowserWindow } from 'electron';
import { spawn } from 'child_process';
import * as fs from 'fs/promises';
import * as path from 'path';
import * as os from 'os';
import log from 'electron-log';
import { createOutputSender } from '../utils/ipc-output-helper';
import { getEnvWithPath } from '../utils/shell-utils';

export interface CareerRepository {
  name: string;
  url: string;
  path: string;
}

/**
 * Clone a single repository
 */
async function cloneRepository(
  repo: CareerRepository,
  sendOutput: (message: string) => void
): Promise<boolean> {
  return new Promise((resolve) => {
    sendOutput(`\nCloning ${repo.name}...`);
    sendOutput(`Repository: ${repo.url}`);
    sendOutput(`Destination: ${repo.path}\n`);

    const cloneProcess = spawn(
      'git',
      ['clone', repo.url, path.basename(repo.path)],
      {
        cwd: path.dirname(repo.path),
        env: getEnvWithPath()
      }
    );

    cloneProcess.stdout.on('data', (data) => {
      sendOutput(data.toString());
    });

    cloneProcess.stderr.on('data', (data) => {
      sendOutput(data.toString());
    });

    cloneProcess.on('close', (code) => {
      if (code === 0) {
        sendOutput(`✓ Successfully cloned ${repo.name}\n`);
        resolve(true);
      } else {
        sendOutput(`✗ Failed to clone ${repo.name} (exit code: ${code})\n`);
        resolve(false);
      }
    });

    cloneProcess.on('error', (error) => {
      sendOutput(`✗ Error cloning ${repo.name}: ${error.message}\n`);
      resolve(false);
    });
  });
}

/**
 * Clone career repositories (canvas-horizon and journey)
 */
export async function cloneCareerRepositories(
  window: BrowserWindow | null,
  baseDirectory: string
): Promise<{ success: boolean; message: string }> {
  const sendOutput = createOutputSender(window, 'career:clone-output');

  try {
    sendOutput('Starting Career repositories clone...');
    sendOutput(`Base directory: ${baseDirectory}\n`);

    // Create base directory
    await fs.mkdir(baseDirectory, { recursive: true });
    sendOutput(`✓ Created directory: ${baseDirectory}\n`);

    // Repository configurations
    const repos: CareerRepository[] = [
      {
        name: 'canvas-horizon',
        url: 'https://github.com/instructure-internal/canvas-horizon.git',
        path: path.join(baseDirectory, 'canvas-horizon')
      },
      {
        name: 'journey',
        url: 'https://github.com/instructure-internal/journey.git',
        path: path.join(baseDirectory, 'journey')
      }
    ];

    // Clone each repository
    for (const repo of repos) {
      // Check if already exists
      try {
        await fs.access(path.join(repo.path, '.git'));
        sendOutput(`✓ ${repo.name} already cloned\n`);
        continue;
      } catch {
        // Doesn't exist, proceed with clone
      }

      const success = await cloneRepository(repo, sendOutput);
      if (!success) {
        return {
          success: false,
          message: `Failed to clone ${repo.name}`
        };
      }
    }

    // Create config file
    sendOutput('\nCreating career configuration file...');
    await createCareerConfig(baseDirectory);
    sendOutput('✓ Configuration file created\n');

    sendOutput('\n✓ Career repositories setup complete!');
    return {
      success: true,
      message: 'Successfully cloned career repositories'
    };
  } catch (error: any) {
    sendOutput(`\n✗ Error: ${error.message}`);
    log.error('Career clone error:', error);
    return {
      success: false,
      message: error.message
    };
  }
}

/**
 * Create career.yml configuration file
 */
export async function createCareerConfig(baseDirectory: string): Promise<void> {
  const configDir = path.join(os.homedir(), '.inst-agent');
  const configPath = path.join(configDir, 'career.yml');

  try {
    // Create directory if it doesn't exist
    await fs.mkdir(configDir, { recursive: true });

    // Create YAML configuration (manual formatting to avoid dependency)
    const yamlContent = `---
career:
  base_code_directory: ${baseDirectory}
  canvas-horizon:
    source: ${path.join(baseDirectory, 'canvas-horizon')}
  journey:
    source: ${path.join(baseDirectory, 'journey')}
`;

    await fs.writeFile(configPath, yamlContent, 'utf8');
    log.info(`Career config created at: ${configPath}`);
  } catch (error) {
    log.error('Error creating career config:', error);
    throw error;
  }
}

/**
 * Check if career config exists and parse it
 */
export async function checkCareerConfig(): Promise<{
  exists: boolean;
  path: string;
  content?: string;
  baseDirectory?: string;
  canvasHorizonPath?: string;
  journeyPath?: string;
}> {
  const configPath = path.join(os.homedir(), '.inst-agent', 'career.yml');

  try {
    const content = await fs.readFile(configPath, 'utf8');

    // Simple YAML parsing for config values
    const baseDirectoryMatch = content.match(/base_code_directory:\s*(.+)/);
    const baseDirectory = baseDirectoryMatch ? baseDirectoryMatch[1].trim() : undefined;

    // Parse canvas-horizon source path
    const canvasHorizonMatch = content.match(/canvas-horizon:\s*\n\s*source:\s*(.+)/);
    const canvasHorizonPath = canvasHorizonMatch ? canvasHorizonMatch[1].trim() : undefined;

    // Parse journey source path
    const journeyMatch = content.match(/journey:\s*\n\s*source:\s*(.+)/);
    const journeyPath = journeyMatch ? journeyMatch[1].trim() : undefined;

    return {
      exists: true,
      path: configPath,
      content,
      baseDirectory,
      canvasHorizonPath,
      journeyPath
    };
  } catch {
    return {
      exists: false,
      path: configPath
    };
  }
}

export interface CareerRepoStatus {
  exists: boolean;
  isValidRepo: boolean;
  remoteUrl: string | null;
  currentBranch: string | null;
  lastCommit: {
    hash: string;
    author: string;
    date: string;
    message: string;
  } | null;
}

export interface CareerRepositoriesStatus {
  baseDirectory: string | null;
  canvasHorizon: CareerRepoStatus;
  journey: CareerRepoStatus;
  configExists: boolean;
  configured: boolean;
}

/**
 * Check status of a single repository
 */
async function checkRepositoryStatus(repoPath: string): Promise<CareerRepoStatus> {
  const { execAsync } = require('../utils/shell-utils');

  try {
    // Check if directory exists
    const fsSync = require('fs');
    if (!fsSync.existsSync(repoPath)) {
      return {
        exists: false,
        isValidRepo: false,
        remoteUrl: null,
        currentBranch: null,
        lastCommit: null
      };
    }

    // Check if it's a valid git repository
    const gitDir = path.join(repoPath, '.git');
    if (!fsSync.existsSync(gitDir)) {
      return {
        exists: true,
        isValidRepo: false,
        remoteUrl: null,
        currentBranch: null,
        lastCommit: null
      };
    }

    // Get remote URL
    let remoteUrl: string | null = null;
    try {
      const remoteResult = await execAsync('git remote get-url origin', {
        cwd: repoPath
      });
      remoteUrl = remoteResult.stdout.trim();
    } catch (error) {
      log.error('Failed to get remote URL:', error);
    }

    // Get current branch
    let currentBranch: string | null = null;
    try {
      const branchResult = await execAsync('git branch --show-current', {
        cwd: repoPath
      });
      currentBranch = branchResult.stdout.trim();
    } catch (error) {
      log.error('Failed to get current branch:', error);
    }

    // Get last commit info
    let lastCommit = null;
    try {
      const commitResult = await execAsync(
        'git log -1 --pretty=format:"%H%n%an%n%aI%n%s"',
        { cwd: repoPath }
      );
      const lines = commitResult.stdout.trim().split('\n');
      if (lines.length >= 4) {
        lastCommit = {
          hash: lines[0].substring(0, 7), // Short hash
          author: lines[1],
          date: lines[2],
          message: lines[3]
        };
      }
    } catch (error) {
      log.error('Failed to get last commit:', error);
    }

    return {
      exists: true,
      isValidRepo: true,
      remoteUrl,
      currentBranch,
      lastCommit
    };
  } catch (error) {
    log.error('Error checking repository status:', error);
    return {
      exists: false,
      isValidRepo: false,
      remoteUrl: null,
      currentBranch: null,
      lastCommit: null
    };
  }
}

/**
 * Check status of career repositories
 */
export async function checkCareerRepositoriesStatus(
  baseDirectory?: string
): Promise<CareerRepositoriesStatus> {
  try {
    // Always check config first
    const config = await checkCareerConfig();

    // Determine which paths to use
    let horizonPath: string | undefined;
    let journeyPath: string | undefined;

    if (config.exists && config.canvasHorizonPath && config.journeyPath) {
      // Use exact paths from config file
      horizonPath = config.canvasHorizonPath;
      journeyPath = config.journeyPath;
      baseDirectory = config.baseDirectory;
    } else if (baseDirectory) {
      // If no config or incomplete config, use provided baseDirectory
      horizonPath = path.join(baseDirectory, 'canvas-horizon');
      journeyPath = path.join(baseDirectory, 'journey');
    }

    // If we still don't have paths, return unconfigured state
    if (!horizonPath || !journeyPath) {
      return {
        baseDirectory: baseDirectory || null,
        canvasHorizon: {
          exists: false,
          isValidRepo: false,
          remoteUrl: null,
          currentBranch: null,
          lastCommit: null
        },
        journey: {
          exists: false,
          isValidRepo: false,
          remoteUrl: null,
          currentBranch: null,
          lastCommit: null
        },
        configExists: config.exists,
        configured: false
      };
    }

    // Check both repositories
    const [canvasHorizon, journey] = await Promise.all([
      checkRepositoryStatus(horizonPath),
      checkRepositoryStatus(journeyPath)
    ]);

    // Mark as configured only if:
    // 1. Config file exists
    // 2. Both repositories exist and are valid git repos
    const configured =
      config.exists &&
      canvasHorizon.isValidRepo &&
      journey.isValidRepo;

    return {
      baseDirectory: baseDirectory || null,
      canvasHorizon,
      journey,
      configExists: config.exists,
      configured
    };
  } catch (error) {
    log.error('Error checking career repositories status:', error);
    throw error;
  }
}

/**
 * Reset a single repository to clean state
 */
async function resetRepository(
  repoPath: string,
  repoName: string,
  sendOutput: (message: string) => void
): Promise<boolean> {
  return new Promise((resolve) => {
    sendOutput(`\nResetting ${repoName}...`);

    const resetProcess = spawn(
      'sh',
      [
        '-c',
        'git clean -fdx && git checkout main 2>/dev/null || git checkout master && git pull origin $(git branch --show-current)'
      ],
      {
        cwd: repoPath,
        env: getEnvWithPath()
      }
    );

    resetProcess.stdout.on('data', (data) => {
      sendOutput(data.toString());
    });

    resetProcess.stderr.on('data', (data) => {
      sendOutput(data.toString());
    });

    resetProcess.on('close', (code) => {
      if (code === 0) {
        sendOutput(`✓ ${repoName} reset successfully\n`);
        resolve(true);
      } else {
        sendOutput(`✗ Failed to reset ${repoName} (exit code: ${code})\n`);
        resolve(false);
      }
    });

    resetProcess.on('error', (error) => {
      sendOutput(`✗ Error resetting ${repoName}: ${error.message}\n`);
      resolve(false);
    });
  });
}

/**
 * Reset career repositories to clean state
 */
export async function resetCareerRepositories(
  window: BrowserWindow | null,
  baseDirectory: string
): Promise<{ success: boolean; message: string }> {
  const sendOutput = createOutputSender(window, 'career:reset-output');

  try {
    sendOutput('Starting career repositories reset...');
    sendOutput('This will:');
    sendOutput('1. Remove all untracked files (git clean -fdx)');
    sendOutput('2. Checkout main/master branch');
    sendOutput('3. Pull latest changes from origin\n');

    const horizonPath = path.join(baseDirectory, 'canvas-horizon');
    const journeyPath = path.join(baseDirectory, 'journey');

    // Reset canvas-horizon
    const horizonSuccess = await resetRepository(horizonPath, 'canvas-horizon', sendOutput);
    if (!horizonSuccess) {
      return {
        success: false,
        message: 'Failed to reset canvas-horizon repository'
      };
    }

    // Reset journey
    const journeySuccess = await resetRepository(journeyPath, 'journey', sendOutput);
    if (!journeySuccess) {
      return {
        success: false,
        message: 'Failed to reset journey repository'
      };
    }

    sendOutput('\n✓ All repositories reset successfully!');
    return {
      success: true,
      message: 'Career repositories reset successfully'
    };
  } catch (error: any) {
    sendOutput(`\n✗ Error: ${error.message}`);
    log.error('Career reset error:', error);
    return {
      success: false,
      message: error.message
    };
  }
}
