/**
 * TASKMATE - Git Service
 * =======================
 * Lokale Git-Operationen über child_process
 */

const { execSync, exec } = require('child_process');
const path = require('path');
const fs = require('fs');
const logger = require('../utils/logger');

/**
 * Konvertiert einen Windows-Pfad zu einem Docker-Container-Pfad
 * z.B. "D:\Projekte\MyApp" -> "/mnt/d/Projekte/MyApp"
 */
function windowsToContainerPath(windowsPath) {
  if (!windowsPath) return null;

  // Bereits ein Container-Pfad?
  if (windowsPath.startsWith('/mnt/')) {
    return windowsPath;
  }

  // Spezialfall: TaskMate-Verzeichnis ist als /app/taskmate-source gemountet
  if (windowsPath === '/home/claude-dev/TaskMate') {
    return '/app/taskmate-source';
  }

  // Windows-Pfad konvertieren (z.B. "C:\foo" oder "C:/foo")
  const normalized = windowsPath.replace(/\\/g, '/');
  const match = normalized.match(/^([a-zA-Z]):[\/](.*)$/);

  if (match) {
    const drive = match[1].toLowerCase();
    const restPath = match[2];
    return `/mnt/${drive}/${restPath}`;
  }

  return windowsPath;
}

/**
 * Konvertiert einen Docker-Container-Pfad zu einem Windows-Pfad
 * z.B. "/mnt/d/Projekte/MyApp" -> "D:\Projekte\MyApp"
 */
function containerToWindowsPath(containerPath) {
  if (!containerPath) return null;

  const match = containerPath.match(/^\/mnt\/([a-z])\/(.*)$/);
  if (match) {
    const drive = match[1].toUpperCase();
    const restPath = match[2].replace(/\//g, '\\');
    return `${drive}:\\${restPath}`;
  }

  return containerPath;
}

/**
 * Prüft, ob ein Pfad existiert und erreichbar ist
 */
function isPathAccessible(localPath) {
  const containerPath = windowsToContainerPath(localPath);
  try {
    // Prüfe ob das übergeordnete Verzeichnis existiert
    const parentDir = path.dirname(containerPath);
    return fs.existsSync(parentDir);
  } catch (error) {
    logger.error('Pfadzugriff fehlgeschlagen:', error);
    return false;
  }
}

/**
 * Prüft, ob ein Verzeichnis ein Git-Repository ist
 */
function isGitRepository(localPath) {
  const containerPath = windowsToContainerPath(localPath);
  try {
    const gitDir = path.join(containerPath, '.git');
    logger.info(`Git-Repository Check: ${localPath} -> ${containerPath} -> ${gitDir}`);
    const exists = fs.existsSync(gitDir);
    logger.info(`Git directory exists: ${exists}`);
    return exists;
  } catch (error) {
    logger.error(`Git-Repository Check failed: ${error.message}`);
    return false;
  }
}

/**
 * Führt einen Git-Befehl aus
 */
function execGitCommand(command, cwd, options = {}) {
  const containerPath = windowsToContainerPath(cwd);
  const timeout = options.timeout || 60000; // 60 Sekunden Standard-Timeout

  try {
    const result = execSync(command, {
      cwd: containerPath,
      encoding: 'utf8',
      timeout,
      maxBuffer: 10 * 1024 * 1024, // 10 MB
      env: {
        ...process.env,
        GIT_TERMINAL_PROMPT: '0', // Keine interaktiven Prompts
        GIT_SSH_COMMAND: 'ssh -o StrictHostKeyChecking=no'
      }
    });
    return { success: true, output: result.trim() };
  } catch (error) {
    logger.error(`Git-Befehl fehlgeschlagen: ${command}`, error.message);
    return {
      success: false,
      error: error.message,
      stderr: error.stderr?.toString() || ''
    };
  }
}

/**
 * Repository klonen
 */
async function cloneRepository(repoUrl, localPath, options = {}) {
  const containerPath = windowsToContainerPath(localPath);
  const branch = options.branch || 'main';

  // Prüfe, ob das Zielverzeichnis bereits existiert
  if (fs.existsSync(containerPath)) {
    if (isGitRepository(localPath)) {
      return { success: false, error: 'Verzeichnis enthält bereits ein Git-Repository' };
    }
    // Verzeichnis existiert, aber ist kein Git-Repo - prüfe ob leer
    const files = fs.readdirSync(containerPath);
    if (files.length > 0) {
      return { success: false, error: 'Verzeichnis ist nicht leer' };
    }
  } else {
    // Erstelle Verzeichnis
    fs.mkdirSync(containerPath, { recursive: true });
  }

  // Gitea-Token für Authentifizierung hinzufügen
  let authUrl = repoUrl;
  const giteaToken = process.env.GITEA_TOKEN;
  if (giteaToken && repoUrl.includes('gitea')) {
    // Füge Token zur URL hinzu: https://token@gitea.example.com/...
    authUrl = repoUrl.replace('https://', `https://${giteaToken}@`);
  }

  const command = `git clone --branch ${branch} "${authUrl}" "${containerPath}"`;

  try {
    execSync(command, {
      encoding: 'utf8',
      timeout: 300000, // 5 Minuten für Clone
      maxBuffer: 50 * 1024 * 1024, // 50 MB
      env: {
        ...process.env,
        GIT_TERMINAL_PROMPT: '0'
      }
    });

    logger.info(`Repository geklont: ${repoUrl} -> ${localPath}`);
    return { success: true, message: 'Repository erfolgreich geklont' };
  } catch (error) {
    logger.error('Clone fehlgeschlagen:', error.message);
    // Bereinige bei Fehler
    try {
      if (fs.existsSync(containerPath)) {
        fs.rmSync(containerPath, { recursive: true, force: true });
      }
    } catch (e) {
      // Ignorieren
    }
    return { success: false, error: error.message };
  }
}

/**
 * Änderungen ziehen (Pull)
 */
function pullChanges(localPath, options = {}) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  const branch = options.branch || '';
  const command = branch ? `git pull origin ${branch}` : 'git pull';

  return execGitCommand(command, localPath);
}

/**
 * Änderungen hochladen (Push)
 */
function pushChanges(localPath, options = {}) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  const branch = options.branch || '';
  const command = branch ? `git push origin ${branch}` : 'git push';

  return execGitCommand(command, localPath, { timeout: 120000 });
}

/**
 * Git-Status abrufen
 */
function getStatus(localPath) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  const result = execGitCommand('git status --porcelain', localPath);

  if (!result.success) {
    return result;
  }

  const lines = result.output.split('\n').filter(l => l.trim());
  const changes = lines.map(line => {
    const status = line.substring(0, 2);
    const file = line.substring(3);
    return { status, file };
  });

  // Zusätzliche Infos
  const branchResult = execGitCommand('git branch --show-current', localPath);
  const aheadBehindResult = execGitCommand('git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"', localPath);

  let ahead = 0;
  let behind = 0;
  if (aheadBehindResult.success) {
    const parts = aheadBehindResult.output.split(/\s+/);
    ahead = parseInt(parts[0]) || 0;
    behind = parseInt(parts[1]) || 0;
  }

  return {
    success: true,
    branch: branchResult.success ? branchResult.output : 'unknown',
    changes,
    hasChanges: changes.length > 0,
    ahead,
    behind,
    isClean: changes.length === 0 && ahead === 0
  };
}

/**
 * Alle Änderungen stagen
 */
function stageAll(localPath) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  return execGitCommand('git add -A', localPath);
}

/**
 * Commit erstellen
 * @param {string} localPath - Pfad zum Repository
 * @param {string} message - Commit-Nachricht
 * @param {object} author - Autor-Informationen { name, email }
 */
function commit(localPath, message, author = null) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  if (!message || message.trim() === '') {
    return { success: false, error: 'Commit-Nachricht erforderlich' };
  }

  // Escape für Shell
  const escapedMessage = message.replace(/"/g, '\\"');

  // Commit-Befehl mit optionalem Autor
  let commitCmd = `git commit -m "${escapedMessage}"`;
  if (author && author.name) {
    const authorName = author.name.replace(/"/g, '\\"');
    const authorEmail = author.email || `${author.name.toLowerCase().replace(/\s+/g, '.')}@taskmate.local`;
    commitCmd = `git commit --author="${authorName} <${authorEmail}>" -m "${escapedMessage}"`;
    logger.info(`Commit mit Autor: ${authorName} <${authorEmail}>`);
  }

  return execGitCommand(commitCmd, localPath);
}

/**
 * Commit-Historie abrufen
 */
function getCommitHistory(localPath, limit = 20) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  const format = '%H|%h|%an|%ae|%at|%s';
  const result = execGitCommand(`git log -${limit} --format="${format}"`, localPath);

  if (!result.success) {
    return result;
  }

  const commits = result.output.split('\n').filter(l => l.trim()).map(line => {
    const [hash, shortHash, author, email, timestamp, subject] = line.split('|');
    return {
      hash,
      shortHash,
      author,
      email,
      date: new Date(parseInt(timestamp) * 1000).toISOString(),
      message: subject
    };
  });

  return { success: true, commits };
}

/**
 * Branches auflisten
 */
function getBranches(localPath) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  const result = execGitCommand('git branch -a', localPath);

  if (!result.success) {
    return result;
  }

  const branches = result.output.split('\n').filter(l => l.trim()).map(line => {
    const isCurrent = line.startsWith('*');
    const name = line.replace(/^\*?\s+/, '').trim();
    const isRemote = name.startsWith('remotes/');
    return { name, isCurrent, isRemote };
  });

  return { success: true, branches };
}

/**
 * Branch wechseln
 */
function checkoutBranch(localPath, branch) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  return execGitCommand(`git checkout ${branch}`, localPath);
}

/**
 * Fetch von Remote
 */
function fetchRemote(localPath) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  return execGitCommand('git fetch --all', localPath, { timeout: 120000 });
}

/**
 * Remote-URL abrufen
 */
function getRemoteUrl(localPath) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  const result = execGitCommand('git remote get-url origin', localPath);

  if (result.success) {
    // Entferne Token aus URL für Anzeige
    let url = result.output;
    url = url.replace(/https:\/\/[^@]+@/, 'https://');
    return { success: true, url };
  }

  return result;
}

/**
 * Git Repository initialisieren
 */
function initRepository(localPath, options = {}) {
  const containerPath = windowsToContainerPath(localPath);
  const branch = options.branch || 'main';

  // Prüfe ob bereits ein Git-Repo existiert
  if (isGitRepository(localPath)) {
    return { success: true, message: 'Git-Repository existiert bereits' };
  }

  // Prüfe ob Verzeichnis existiert
  if (!fs.existsSync(containerPath)) {
    return { success: false, error: 'Verzeichnis existiert nicht' };
  }

  // Git init ausführen
  const initResult = execGitCommand(`git init -b ${branch}`, localPath);
  if (!initResult.success) {
    return initResult;
  }

  logger.info(`Git-Repository initialisiert: ${localPath}`);
  return { success: true, message: 'Git-Repository initialisiert' };
}

/**
 * Remote hinzufügen oder aktualisieren
 */
function setRemote(localPath, remoteUrl, remoteName = 'origin') {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  // Gitea-Token für Authentifizierung hinzufügen
  let authUrl = remoteUrl;
  const giteaToken = process.env.GITEA_TOKEN;
  if (giteaToken && (remoteUrl.includes('gitea') || remoteUrl.includes('aegis-sight'))) {
    authUrl = remoteUrl.replace('https://', `https://${giteaToken}@`);
  }

  // Prüfe ob Remote bereits existiert
  const checkResult = execGitCommand(`git remote get-url ${remoteName}`, localPath);

  if (checkResult.success) {
    // Remote existiert - aktualisieren
    const result = execGitCommand(`git remote set-url ${remoteName} "${authUrl}"`, localPath);
    if (result.success) {
      logger.info(`Remote '${remoteName}' aktualisiert: ${remoteUrl}`);
      return { success: true, message: 'Remote aktualisiert' };
    }
    return result;
  } else {
    // Remote existiert nicht - hinzufügen
    const result = execGitCommand(`git remote add ${remoteName} "${authUrl}"`, localPath);
    if (result.success) {
      logger.info(`Remote '${remoteName}' hinzugefügt: ${remoteUrl}`);
      return { success: true, message: 'Remote hinzugefügt' };
    }
    return result;
  }
}

/**
 * Prüft ob ein Remote existiert
 */
function hasRemote(localPath, remoteName = 'origin') {
  if (!isGitRepository(localPath)) {
    return false;
  }

  const result = execGitCommand(`git remote get-url ${remoteName}`, localPath);
  return result.success;
}

/**
 * Initialen Push mit Upstream-Tracking
 */
function pushWithUpstream(localPath, targetBranch = null, remoteName = 'origin', force = false) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  // Prüfe ob Commits existieren
  let logResult = execGitCommand('git rev-parse HEAD', localPath);
  if (!logResult.success) {
    // Keine Commits - erstelle einen initialen Commit
    const statusResult = execGitCommand('git status --porcelain', localPath);
    if (statusResult.success && statusResult.output.trim()) {
      // Es gibt Dateien - stage und commit
      const stageResult = stageAll(localPath);
      if (!stageResult.success) {
        return { success: false, error: 'Staging fehlgeschlagen: ' + stageResult.error };
      }

      const commitResult = commit(localPath, 'Initial commit');
      if (!commitResult.success) {
        return { success: false, error: 'Initial commit fehlgeschlagen: ' + commitResult.error };
      }

      logger.info('Initialer Commit erstellt vor Push');

      // Verifiziere dass der Commit erstellt wurde
      logResult = execGitCommand('git rev-parse HEAD', localPath);
      if (!logResult.success) {
        return { success: false, error: 'Commit wurde erstellt aber HEAD existiert nicht' };
      }
    } else {
      return { success: false, error: 'Keine Commits und keine Dateien zum Committen vorhanden' };
    }
  }

  // Aktuellen (lokalen) Branch ermitteln NACH dem Commit
  let localBranch = null;
  const branchResult = execGitCommand('git branch --show-current', localPath);
  if (branchResult.success && branchResult.output) {
    localBranch = branchResult.output;
    logger.info(`Lokaler Branch: ${localBranch}`);
  } else {
    // Fallback: Branch aus git branch auslesen
    const branchListResult = execGitCommand('git branch', localPath);
    logger.info(`Branch-Liste: ${branchListResult.output}`);

    if (branchListResult.success && branchListResult.output) {
      const lines = branchListResult.output.split('\n');
      for (const line of lines) {
        if (line.includes('*')) {
          localBranch = line.replace('*', '').trim();
          break;
        }
      }
    }

    if (!localBranch) {
      // Letzter Fallback: Versuche den Branch zu erstellen
      logger.info('Kein Branch gefunden, erstelle main');
      execGitCommand('git checkout -b main', localPath);
      localBranch = 'main';
    }
  }

  // Wenn kein Ziel-Branch angegeben, verwende lokalen Branch-Namen
  const remoteBranch = targetBranch || localBranch;

  logger.info(`Push: lokaler Branch '${localBranch}' → Remote Branch '${remoteBranch}'${force ? ' (FORCE)' : ''}`);

  // Push mit -u für Upstream-Tracking
  // Format: git push -u origin localBranch:remoteBranch
  const forceFlag = force ? ' --force' : '';
  let pushCommand;
  if (localBranch === remoteBranch) {
    pushCommand = `git push -u${forceFlag} ${remoteName} ${localBranch}`;
  } else {
    // Branch-Mapping: lokalen Branch auf anderen Remote-Branch pushen
    pushCommand = `git push -u${forceFlag} ${remoteName} ${localBranch}:${remoteBranch}`;
  }

  const pushResult = execGitCommand(pushCommand, localPath, { timeout: 120000 });

  if (!pushResult.success) {
    logger.error(`Push fehlgeschlagen: ${pushResult.error}`);
  }

  // Branch-Namen im Ergebnis inkludieren für Default-Branch Aktualisierung
  pushResult.localBranch = localBranch;
  pushResult.branch = remoteBranch; // Remote-Branch für Gitea Default-Branch

  return pushResult;
}

/**
 * Repository für Gitea vorbereiten (init, remote, initial commit)
 */
function prepareForGitea(localPath, remoteUrl, options = {}) {
  const branch = options.branch || 'main';
  const containerPath = windowsToContainerPath(localPath);

  // 1. Prüfe ob Git-Repo existiert, wenn nicht initialisieren
  if (!isGitRepository(localPath)) {
    const initResult = initRepository(localPath, { branch });
    if (!initResult.success) {
      return initResult;
    }
  }

  // 2. Remote hinzufügen/aktualisieren
  const remoteResult = setRemote(localPath, remoteUrl);
  if (!remoteResult.success) {
    return remoteResult;
  }

  // 3. Prüfe ob Commits existieren
  const logResult = execGitCommand('git rev-parse HEAD', localPath);
  if (!logResult.success) {
    // Keine Commits - erstelle initialen Commit wenn Dateien vorhanden
    const statusResult = execGitCommand('git status --porcelain', localPath);
    if (statusResult.success && statusResult.output.trim()) {
      // Es gibt Dateien - stage und commit
      const stageResult = stageAll(localPath);
      if (!stageResult.success) {
        return stageResult;
      }

      const commitResult = commit(localPath, 'Initial commit');
      if (!commitResult.success) {
        return commitResult;
      }
    }
  }

  logger.info(`Repository für Gitea vorbereitet: ${localPath} -> ${remoteUrl}`);
  return { success: true, message: 'Repository für Gitea vorbereitet' };
}

/**
 * Branch umbenennen
 */
function renameBranch(localPath, oldName, newName) {
  if (!isGitRepository(localPath)) {
    return { success: false, error: 'Kein Git-Repository' };
  }

  // Validiere neuen Branch-Namen
  if (!newName || !/^[a-zA-Z0-9_\-]+$/.test(newName)) {
    return { success: false, error: 'Ungültiger Branch-Name. Nur Buchstaben, Zahlen, Unterstriche und Bindestriche erlaubt.' };
  }

  // Prüfe ob wir auf dem zu umbenennenden Branch sind
  const branchResult = execGitCommand('git branch --show-current', localPath);
  const currentBranch = branchResult.success ? branchResult.output : null;

  if (currentBranch !== oldName) {
    return { success: false, error: `Bitte wechsle zuerst zum Branch "${oldName}" bevor du ihn umbenennst.` };
  }

  // Branch umbenennen
  const renameResult = execGitCommand(`git branch -m ${oldName} ${newName}`, localPath);

  if (!renameResult.success) {
    logger.error(`Branch umbenennen fehlgeschlagen: ${renameResult.error}`);
    return renameResult;
  }

  logger.info(`Branch umbenannt: ${oldName} → ${newName}`);
  return { success: true, oldName, newName, message: `Branch "${oldName}" zu "${newName}" umbenannt` };
}

module.exports = {
  windowsToContainerPath,
  containerToWindowsPath,
  isPathAccessible,
  isGitRepository,
  cloneRepository,
  pullChanges,
  pushChanges,
  getStatus,
  stageAll,
  commit,
  getCommitHistory,
  getBranches,
  checkoutBranch,
  fetchRemote,
  getRemoteUrl,
  initRepository,
  setRemote,
  hasRemote,
  pushWithUpstream,
  prepareForGitea,
  renameBranch
};
