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 LearnerHubRepoStatus {
  exists: boolean;
  isValidRepo: boolean;
  remoteUrl: string | null;
  currentBranch: string | null;
  lastCommit: {
    hash: string;
    author: string;
    date: string;
    message: string;
  } | null;
}

export interface LearnerHubRepositoriesStatus {
  baseDirectory: string | null;
  learnerHub: LearnerHubRepoStatus;
  configExists: boolean;
  configured: boolean;
}

const REPO_NAME = 'learner-hub';
const REPO_URL = 'https://github.com/instructure-internal/learner-hub.git';
const CONFIG_FILE = 'learner-hub.yml';

/**
 * Clone the learner-hub repository
 */
export async function cloneLearnerHubRepository(
  window: BrowserWindow | null,
  baseDirectory: string
): Promise<{ success: boolean; message: string }> {
  const sendOutput = createOutputSender(window, 'learner-hub:clone-output');

  try {
    sendOutput('Starting Learner Hub repository clone...');
    sendOutput(`Base directory: ${baseDirectory}\n`);

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

    const repoPath = path.join(baseDirectory, REPO_NAME);

    // Check if already cloned
    try {
      await fs.access(path.join(repoPath, '.git'));
      sendOutput(`✓ ${REPO_NAME} already cloned\n`);
    } catch {
      sendOutput(`\nCloning ${REPO_NAME}...`);
      sendOutput(`Repository: ${REPO_URL}`);
      sendOutput(`Destination: ${repoPath}\n`);

      const success = await new Promise<boolean>((resolve) => {
        const cloneProcess = spawn('git', ['clone', REPO_URL, REPO_NAME], {
          cwd: baseDirectory,
          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);
        });
      });

      if (!success) {
        return { success: false, message: `Failed to clone ${REPO_NAME}` };
      }
    }

    sendOutput('\nCreating Learner Hub configuration file...');
    await createLearnerHubConfig(baseDirectory);
    sendOutput('✓ Configuration file created\n');

    sendOutput('\n✓ Learner Hub repository setup complete!');
    return { success: true, message: 'Successfully cloned learner-hub repository' };
  } catch (error: any) {
    sendOutput(`\n✗ Error: ${error.message}`);
    log.error('Learner Hub clone error:', error);
    return { success: false, message: error.message };
  }
}

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

  await fs.mkdir(configDir, { recursive: true });

  const yamlContent = `---
learner-hub:
  base_code_directory: ${baseDirectory}
  learner-hub:
    source: ${path.join(baseDirectory, REPO_NAME)}
`;

  await fs.writeFile(configPath, yamlContent, 'utf8');
  log.info(`Learner Hub config created at: ${configPath}`);
}

/**
 * Check if learner-hub config exists and parse it
 */
export async function checkLearnerHubConfig(): Promise<{
  exists: boolean;
  path: string;
  content?: string;
  baseDirectory?: string;
  learnerHubPath?: string;
}> {
  const configPath = path.join(os.homedir(), '.inst-agent', CONFIG_FILE);

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

    const baseDirectoryMatch = content.match(/base_code_directory:\s*(.+)/);
    const baseDirectory = baseDirectoryMatch ? baseDirectoryMatch[1].trim() : undefined;

    const learnerHubMatch = content.match(/learner-hub:\s*\n\s*source:\s*(.+)/);
    const learnerHubPath = learnerHubMatch ? learnerHubMatch[1].trim() : undefined;

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

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

  try {
    if (!fsSync.existsSync(repoPath)) {
      return { exists: false, isValidRepo: false, remoteUrl: null, currentBranch: null, lastCommit: null };
    }

    if (!fsSync.existsSync(path.join(repoPath, '.git'))) {
      return { exists: true, isValidRepo: false, remoteUrl: null, currentBranch: null, lastCommit: null };
    }

    let remoteUrl: string | null = null;
    try {
      const r = await execAsync('git remote get-url origin', { cwd: repoPath });
      remoteUrl = r.stdout.trim();
    } catch { /* ignore */ }

    let currentBranch: string | null = null;
    try {
      const r = await execAsync('git branch --show-current', { cwd: repoPath });
      currentBranch = r.stdout.trim();
    } catch { /* ignore */ }

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

    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 learner-hub repository
 */
export async function checkLearnerHubRepositoryStatus(
  baseDirectory?: string
): Promise<LearnerHubRepositoriesStatus> {
  try {
    const config = await checkLearnerHubConfig();

    let repoPath: string | undefined;

    if (config.exists && config.learnerHubPath) {
      repoPath = config.learnerHubPath;
      baseDirectory = config.baseDirectory;
    } else if (baseDirectory) {
      repoPath = path.join(baseDirectory, REPO_NAME);
    }

    if (!repoPath) {
      return {
        baseDirectory: baseDirectory || null,
        learnerHub: { exists: false, isValidRepo: false, remoteUrl: null, currentBranch: null, lastCommit: null },
        configExists: config.exists,
        configured: false
      };
    }

    const learnerHub = await checkRepositoryStatus(repoPath);
    const configured = config.exists && learnerHub.isValidRepo;

    return { baseDirectory: baseDirectory || null, learnerHub, configExists: config.exists, configured };
  } catch (error) {
    log.error('Error checking learner-hub repository status:', error);
    throw error;
  }
}

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

  try {
    sendOutput('Starting Learner Hub repository 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 repoPath = path.join(baseDirectory, REPO_NAME);

    const success = await new Promise<boolean>((resolve) => {
      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(`✓ ${REPO_NAME} reset successfully\n`);
          resolve(true);
        } else {
          sendOutput(`✗ Failed to reset ${REPO_NAME} (exit code: ${code})\n`);
          resolve(false);
        }
      });

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

    if (!success) {
      return { success: false, message: `Failed to reset ${REPO_NAME} repository` };
    }

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