import express from 'express';
import { exec, spawn } from 'child_process';
import { promisify } from 'util';
import path from 'path';
import { promises as fs } from 'fs';
import { extractProjectDirectory } from '../projects.js';
import { queryClaudeSDK } from '../claude-sdk.js';
import { spawnCursor } from '../cursor-cli.js';

const router = express.Router();
const execAsync = promisify(exec);

function spawnAsync(command, args, options = {}) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, {
      ...options,
      shell: false,
    });

    let stdout = '';
    let stderr = '';

    child.stdout.on('data', (data) => {
      stdout += data.toString();
    });

    child.stderr.on('data', (data) => {
      stderr += data.toString();
    });

    child.on('error', (error) => {
      reject(error);
    });

    child.on('close', (code) => {
      if (code === 0) {
        resolve({ stdout, stderr });
        return;
      }

      const error = new Error(`Command failed: ${command} ${args.join(' ')}`);
      error.code = code;
      error.stdout = stdout;
      error.stderr = stderr;
      reject(error);
    });
  });
}

/**
 * Relay-aware git command execution.
 * In cloud mode with relay connected, sends git command to user's machine.
 * Otherwise falls back to local execAsync.
 */
async function relayGit(req, gitArgs, cwd, timeout = 15000) {
  if (req.isCloud && req.hasRelay()) {
    const result = await req.sendRelay('shell-command', { command: `git ${gitArgs}`, cwd }, timeout);
    return { stdout: result.stdout || result.output || '', stderr: result.stderr || '' };
  }
  return execAsync(`git ${gitArgs}`, { cwd });
}

/**
 * Relay-aware spawn (for commands needing array args like git log).
 * Falls back to local spawnAsync when no relay.
 */
async function relaySpawn(req, command, args, options = {}, timeout = 15000) {
  if (req.isCloud && req.hasRelay()) {
    const fullCommand = `${command} ${args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ')}`;
    const result = await req.sendRelay('shell-command', { command: fullCommand, cwd: options.cwd }, timeout);
    return { stdout: result.stdout || result.output || '', stderr: result.stderr || '' };
  }
  return spawnAsync(command, args, options);
}

/**
 * Relay-aware git repository validation.
 */
async function validateGitRepo(req, projectPath) {
  if (req.isCloud && req.hasRelay()) {
    try {
      const result = await req.sendRelay('shell-command', {
        command: 'git rev-parse --is-inside-work-tree',
        cwd: projectPath
      }, 10000);
      const output = (result.stdout || result.output || '').trim();
      if (output !== 'true') {
        throw new Error('Not inside a git work tree');
      }
      await req.sendRelay('shell-command', {
        command: 'git rev-parse --show-toplevel',
        cwd: projectPath
      }, 10000);
    } catch {
      throw new Error('Not a git repository. This directory does not contain a .git folder. Initialize a git repository with "git init" to use source control features.');
    }
    return;
  }
  // Local validation
  return validateGitRepository(projectPath);
}

// Helper function to get the actual project path from the encoded project name
async function getActualProjectPath(projectName) {
  try {
    return await extractProjectDirectory(projectName);
  } catch (error) {
    // project directory extraction error
    // Fallback to the old method
    return projectName.replace(/-/g, '/');
  }
}

// Helper function to strip git diff headers
function stripDiffHeaders(diff) {
  if (!diff) return '';

  const lines = diff.split('\n');
  const filteredLines = [];
  let startIncluding = false;

  for (const line of lines) {
    // Skip all header lines including diff --git, index, file mode, and --- / +++ file paths
    if (line.startsWith('diff --git') ||
        line.startsWith('index ') ||
        line.startsWith('new file mode') ||
        line.startsWith('deleted file mode') ||
        line.startsWith('---') ||
        line.startsWith('+++')) {
      continue;
    }

    // Start including lines from @@ hunk headers onwards
    if (line.startsWith('@@') || startIncluding) {
      startIncluding = true;
      filteredLines.push(line);
    }
  }

  return filteredLines.join('\n');
}

// Helper function to validate git repository
async function validateGitRepository(projectPath) {
  try {
    // Check if directory exists
    await fs.access(projectPath);
  } catch {
    throw new Error(`Project path not found: ${projectPath}`);
  }

  try {
    // Allow any directory that is inside a work tree (repo root or nested folder).
    const { stdout: insideWorkTreeOutput } = await execAsync('git rev-parse --is-inside-work-tree', { cwd: projectPath });
    const isInsideWorkTree = insideWorkTreeOutput.trim() === 'true';
    if (!isInsideWorkTree) {
      throw new Error('Not inside a git work tree');
    }

    // Ensure git can resolve the repository root for this directory.
    await execAsync('git rev-parse --show-toplevel', { cwd: projectPath });
  } catch {
    throw new Error('Not a git repository. This directory does not contain a .git folder. Initialize a git repository with "git init" to use source control features.');
  }
}

// Get git status for a project
router.get('/status', async (req, res) => {
  const { project } = req.query;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    // Cloud mode guard
    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    // Validate git repository
    await validateGitRepo(req, projectPath);

    // Get current branch - handle case where there are no commits yet
    let branch = 'main';
    let hasCommits = true;
    try {
      const { stdout: branchOutput } = await relayGit(req, 'rev-parse --abbrev-ref HEAD', projectPath);
      branch = branchOutput.trim();
    } catch (error) {
      if (error.message.includes('unknown revision') || error.message.includes('ambiguous argument')) {
        hasCommits = false;
        branch = 'main';
      } else {
        throw error;
      }
    }

    // Get git status
    const { stdout: statusOutput } = await relayGit(req, 'status --porcelain', projectPath);

    const modified = [];
    const added = [];
    const deleted = [];
    const untracked = [];

    statusOutput.split('\n').forEach(line => {
      if (!line.trim()) return;

      const status = line.substring(0, 2);
      const file = line.substring(3);

      if (status === 'M ' || status === ' M' || status === 'MM') {
        modified.push(file);
      } else if (status === 'A ' || status === 'AM') {
        added.push(file);
      } else if (status === 'D ' || status === ' D') {
        deleted.push(file);
      } else if (status === '??') {
        untracked.push(file);
      }
    });

    res.json({
      branch,
      hasCommits,
      modified,
      added,
      deleted,
      untracked
    });
  } catch (error) {
    // git status error
    const isNotRepo = error.message?.includes('not a git repository');
    res.json({
      error: isNotRepo ? 'Not a git repository' : 'Git operation failed'
    });
  }
});

// Get diff for a specific file
router.get('/diff', async (req, res) => {
  const { project, file } = req.query;

  if (!project || !file) {
    return res.status(400).json({ error: 'Project name and file path are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: statusOutput } = await relayGit(req, `status --porcelain "${file}"`, projectPath);
    const isUntracked = statusOutput.startsWith('??');
    const isDeleted = statusOutput.trim().startsWith('D ') || statusOutput.trim().startsWith(' D');

    let diff;
    if (isUntracked) {
      if (req.isCloud && req.hasRelay()) {
        // Read file via relay for untracked files
        try {
          const result = await req.sendRelay('file-read', { filePath: `${projectPath}/${file}` }, 15000);
          const fileContent = result.content || '';
          const lines = fileContent.split('\n');
          diff = `--- /dev/null\n+++ b/${file}\n@@ -0,0 +1,${lines.length} @@\n` +
                 lines.map(line => `+${line}`).join('\n');
        } catch {
          diff = `New file: ${file}\n(Unable to read file content)`;
        }
      } else {
        const filePath = path.join(projectPath, file);
        const stats = await fs.stat(filePath);
        if (stats.isDirectory()) {
          diff = `Directory: ${file}\n(Cannot show diff for directories)`;
        } else {
          const fileContent = await fs.readFile(filePath, 'utf-8');
          const lines = fileContent.split('\n');
          diff = `--- /dev/null\n+++ b/${file}\n@@ -0,0 +1,${lines.length} @@\n` +
                 lines.map(line => `+${line}`).join('\n');
        }
      }
    } else if (isDeleted) {
      const { stdout: fileContent } = await relayGit(req, `show HEAD:"${file}"`, projectPath);
      const lines = fileContent.split('\n');
      diff = `--- a/${file}\n+++ /dev/null\n@@ -1,${lines.length} +0,0 @@\n` +
             lines.map(line => `-${line}`).join('\n');
    } else {
      const { stdout: unstagedDiff } = await relayGit(req, `diff -- "${file}"`, projectPath);
      if (unstagedDiff) {
        diff = stripDiffHeaders(unstagedDiff);
      } else {
        const { stdout: stagedDiff } = await relayGit(req, `diff --cached -- "${file}"`, projectPath);
        diff = stripDiffHeaders(stagedDiff) || '';
      }
    }

    res.json({ diff });
  } catch (error) {
    res.json({ error: 'Git operation failed' });
  }
});

// Get file content with diff information for CodeEditor
router.get('/file-with-diff', async (req, res) => {
  const { project, file } = req.query;

  if (!project || !file) {
    return res.status(400).json({ error: 'Project name and file path are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: statusOutput } = await relayGit(req, `status --porcelain "${file}"`, projectPath);
    const isUntracked = statusOutput.startsWith('??');
    const isDeleted = statusOutput.trim().startsWith('D ') || statusOutput.trim().startsWith(' D');

    let currentContent = '';
    let oldContent = '';

    if (isDeleted) {
      const { stdout: headContent } = await relayGit(req, `show HEAD:"${file}"`, projectPath);
      oldContent = headContent;
      currentContent = headContent;
    } else {
      // Get current file content
      if (req.isCloud && req.hasRelay()) {
        try {
          const result = await req.sendRelay('file-read', { filePath: `${projectPath}/${file}` }, 15000);
          currentContent = result.content || '';
        } catch {
          return res.status(500).json({ error: 'Failed to read file via relay' });
        }
      } else {
        const filePath = path.join(projectPath, file);
        const stats = await fs.stat(filePath);
        if (stats.isDirectory()) {
          return res.status(400).json({ error: 'Cannot show diff for directories' });
        }
        currentContent = await fs.readFile(filePath, 'utf-8');
      }

      if (!isUntracked) {
        try {
          const { stdout: headContent } = await relayGit(req, `show HEAD:"${file}"`, projectPath);
          oldContent = headContent;
        } catch (error) {
          oldContent = '';
        }
      }
    }

    res.json({ currentContent, oldContent, isDeleted, isUntracked });
  } catch (error) {
    res.json({ error: 'Git operation failed' });
  }
});

// Create initial commit
router.post('/initial-commit', async (req, res) => {
  const { project } = req.body;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    try {
      await relayGit(req, 'rev-parse HEAD', projectPath);
      return res.status(400).json({ error: 'Repository already has commits. Use regular commit instead.' });
    } catch (error) {
      // No HEAD - good, we can create initial commit
    }

    await relayGit(req, 'add .', projectPath);
    const { stdout } = await relayGit(req, 'commit -m "Initial commit"', projectPath);

    res.json({ success: true, output: stdout, message: 'Initial commit created successfully' });
  } catch (error) {
    if (error.message.includes('nothing to commit')) {
      return res.status(400).json({
        error: 'Nothing to commit',
        details: 'No files found in the repository. Add some files first.'
      });
    }
    res.status(500).json({ error: 'Git operation failed' });
  }
});

// Commit changes
router.post('/commit', async (req, res) => {
  const { project, message, files } = req.body;

  if (!project || !message || !files || files.length === 0) {
    return res.status(400).json({ error: 'Project name, commit message, and files are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    for (const file of files) {
      await relayGit(req, `add "${file}"`, projectPath);
    }

    const { stdout } = await relayGit(req, `commit -m "${message.replace(/"/g, '\\"')}"`, projectPath);

    res.json({ success: true, output: stdout });
  } catch (error) {
    res.status(500).json({ error: 'Git operation failed' });
  }
});

// Get list of branches
router.get('/branches', async (req, res) => {
  const { project } = req.query;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout } = await relayGit(req, 'branch -a', projectPath);
    
    // Parse branches
    const branches = stdout
      .split('\n')
      .map(branch => branch.trim())
      .filter(branch => branch && !branch.includes('->')) // Remove empty lines and HEAD pointer
      .map(branch => {
        // Remove asterisk from current branch
        if (branch.startsWith('* ')) {
          return branch.substring(2);
        }
        // Remove remotes/ prefix
        if (branch.startsWith('remotes/origin/')) {
          return branch.substring(15);
        }
        return branch;
      })
      .filter((branch, index, self) => self.indexOf(branch) === index); // Remove duplicates
    
    res.json({ branches });
  } catch (error) {
    // git branches error
    res.json({ error: 'Git operation failed' });
  }
});

// Checkout branch
router.post('/checkout', async (req, res) => {
  const { project, branch } = req.body;

  if (!project || !branch) {
    return res.status(400).json({ error: 'Project name and branch are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    const { stdout } = await relayGit(req, `checkout "${branch}"`, projectPath);

    res.json({ success: true, output: stdout });
  } catch (error) {
    res.status(500).json({ error: 'Git operation failed' });
  }
});

// Create new branch
router.post('/create-branch', async (req, res) => {
  const { project, branch } = req.body;

  if (!project || !branch) {
    return res.status(400).json({ error: 'Project name and branch name are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    const { stdout } = await relayGit(req, `checkout -b "${branch}"`, projectPath);

    res.json({ success: true, output: stdout });
  } catch (error) {
    res.status(500).json({ error: 'Git operation failed' });
  }
});

// Get recent commits
router.get('/commits', async (req, res) => {
  const { project, limit = 10 } = req.query;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);
    const parsedLimit = Number.parseInt(String(limit), 10);
    const safeLimit = Number.isFinite(parsedLimit) && parsedLimit > 0
      ? Math.min(parsedLimit, 100)
      : 10;

    const { stdout } = await relaySpawn(
      req,
      'git',
      ['log', '--pretty=format:%H|%an|%ae|%ad|%s', '--date=relative', '-n', String(safeLimit)],
      { cwd: projectPath },
    );

    const commits = stdout
      .split('\n')
      .filter(line => line.trim())
      .map(line => {
        const [hash, author, email, date, ...messageParts] = line.split('|');
        return { hash, author, email, date, message: messageParts.join('|') };
      });

    for (const commit of commits) {
      try {
        const { stdout: stats } = await relayGit(req, `show --stat --format='' ${commit.hash}`, projectPath);
        commit.stats = stats.trim().split('\n').pop();
      } catch (error) {
        commit.stats = '';
      }
    }

    res.json({ commits });
  } catch (error) {
    res.json({ error: 'Git operation failed' });
  }
});

// Get diff for a specific commit
router.get('/commit-diff', async (req, res) => {
  const { project, commit } = req.query;

  if (!project || !commit) {
    return res.status(400).json({ error: 'Project name and commit hash are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    const { stdout } = await relayGit(req, `show ${commit}`, projectPath);

    res.json({ diff: stdout });
  } catch (error) {
    res.json({ error: 'Git operation failed' });
  }
});

// Generate commit message based on staged changes using AI
router.post('/generate-commit-message', async (req, res) => {
  const { project, files, provider = 'claude' } = req.body;

  if (!project || !files || files.length === 0) {
    return res.status(400).json({ error: 'Project name and files are required' });
  }

  if (!['claude', 'cursor'].includes(provider)) {
    return res.status(400).json({ error: 'provider must be "claude" or "cursor"' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    let diffContext = '';
    for (const file of files) {
      try {
        const { stdout } = await relayGit(req, `diff HEAD -- "${file}"`, projectPath);
        if (stdout) {
          diffContext += `\n--- ${file} ---\n${stdout}`;
        }
      } catch (error) {
        // diff error
      }
    }

    if (!diffContext.trim()) {
      for (const file of files) {
        try {
          if (req.isCloud && req.hasRelay()) {
            const result = await req.sendRelay('file-read', { filePath: `${projectPath}/${file}` }, 15000);
            const content = result.content || '';
            diffContext += `\n--- ${file} (new file) ---\n${content.substring(0, 1000)}\n`;
          } else {
            const filePath = path.join(projectPath, file);
            const stats = await fs.stat(filePath);
            if (!stats.isDirectory()) {
              const content = await fs.readFile(filePath, 'utf-8');
              diffContext += `\n--- ${file} (new file) ---\n${content.substring(0, 1000)}\n`;
            } else {
              diffContext += `\n--- ${file} (new directory) ---\n`;
            }
          }
        } catch (error) {
          // file read error
        }
      }
    }

    const message = await generateCommitMessageWithAI(files, diffContext, provider, projectPath);

    res.json({ message });
  } catch (error) {
    res.status(500).json({ error: 'Git operation failed' });
  }
});

/**
 * Generates a commit message using AI (Claude SDK or Cursor CLI)
 * @param {Array<string>} files - List of changed files
 * @param {string} diffContext - Git diff content
 * @param {string} provider - 'claude' or 'cursor'
 * @param {string} projectPath - Project directory path
 * @returns {Promise<string>} Generated commit message
 */
async function generateCommitMessageWithAI(files, diffContext, provider, projectPath) {
  // Create the prompt
  const prompt = `Generate a conventional commit message for these changes.

REQUIREMENTS:
- Format: type(scope): subject
- Include body explaining what changed and why
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Subject under 50 chars, body wrapped at 72 chars
- Focus on user-facing changes, not implementation details
- Consider what's being added AND removed
- Return ONLY the commit message (no markdown, explanations, or code blocks)

FILES CHANGED:
${files.map(f => `- ${f}`).join('\n')}

DIFFS:
${diffContext.substring(0, 4000)}

Generate the commit message:`;

  try {
    // Create a simple writer that collects the response
    let responseText = '';
    const writer = {
      send: (data) => {
        try {
          const parsed = typeof data === 'string' ? JSON.parse(data) : data;

          // Handle different message formats from Claude SDK and Cursor CLI
          // Claude SDK sends: {type: 'claude-response', data: {message: {content: [...]}}}
          if (parsed.type === 'claude-response' && parsed.data) {
            const message = parsed.data.message || parsed.data;
            if (message.content && Array.isArray(message.content)) {
              // Extract text from content array
              for (const item of message.content) {
                if (item.type === 'text' && item.text) {
                  responseText += item.text;
                }
              }
            }
          }
          // Cursor CLI sends: {type: 'cursor-output', output: '...'}
          else if (parsed.type === 'cursor-output' && parsed.output) {
            responseText += parsed.output;
          }
          // Also handle direct text messages
          else if (parsed.type === 'text' && parsed.text) {
            responseText += parsed.text;
          }
        } catch (e) {
          // Ignore parse errors
        }
      },
      setSessionId: () => {}, // No-op for this use case
    };


    // Call the appropriate agent
    if (provider === 'claude') {
      await queryClaudeSDK(prompt, {
        cwd: projectPath,
        permissionMode: 'bypassPermissions',
        model: 'sonnet'
      }, writer);
    } else if (provider === 'cursor') {
      await spawnCursor(prompt, {
        cwd: projectPath,
        skipPermissions: true
      }, writer);
    }


    // Clean up the response
    const cleanedMessage = cleanCommitMessage(responseText);

    return cleanedMessage || 'chore: update files';
  } catch (error) {
    // AI commit message error
    // Fallback to simple message
    return `chore: update ${files.length} file${files.length !== 1 ? 's' : ''}`;
  }
}

/**
 * Cleans the AI-generated commit message by removing markdown, code blocks, and extra formatting
 * @param {string} text - Raw AI response
 * @returns {string} Clean commit message
 */
function cleanCommitMessage(text) {
  if (!text || !text.trim()) {
    return '';
  }

  let cleaned = text.trim();

  // Remove markdown code blocks
  cleaned = cleaned.replace(/```[a-z]*\n/g, '');
  cleaned = cleaned.replace(/```/g, '');

  // Remove markdown headers
  cleaned = cleaned.replace(/^#+\s*/gm, '');

  // Remove leading/trailing quotes
  cleaned = cleaned.replace(/^["']|["']$/g, '');

  // If there are multiple lines, take everything (subject + body)
  // Just clean up extra blank lines
  cleaned = cleaned.replace(/\n{3,}/g, '\n\n');

  // Remove any explanatory text before the actual commit message
  // Look for conventional commit pattern and start from there
  const conventionalCommitMatch = cleaned.match(/(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+?\))?:.+/s);
  if (conventionalCommitMatch) {
    cleaned = cleaned.substring(cleaned.indexOf(conventionalCommitMatch[0]));
  }

  return cleaned.trim();
}

// Get remote status (ahead/behind commits with smart remote detection)
router.get('/remote-status', async (req, res) => {
  const { project } = req.query;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: currentBranch } = await relayGit(req, 'rev-parse --abbrev-ref HEAD', projectPath);
    const branch = currentBranch.trim();

    let trackingBranch;
    let remoteName;
    try {
      const { stdout } = await relayGit(req, `rev-parse --abbrev-ref ${branch}@{upstream}`, projectPath);
      trackingBranch = stdout.trim();
      remoteName = trackingBranch.split('/')[0];
    } catch (error) {
      let hasRemote = false;
      let remoteName = null;
      try {
        const { stdout } = await relayGit(req, 'remote', projectPath);
        const remotes = stdout.trim().split('\n').filter(r => r.trim());
        if (remotes.length > 0) {
          hasRemote = true;
          remoteName = remotes.includes('origin') ? 'origin' : remotes[0];
        }
      } catch (remoteError) {
        // No remotes configured
      }

      return res.json({
        hasRemote,
        hasUpstream: false,
        branch,
        remoteName,
        message: 'No remote tracking branch configured'
      });
    }

    const { stdout: countOutput } = await relayGit(req, `rev-list --count --left-right ${trackingBranch}...HEAD`, projectPath);

    const [behind, ahead] = countOutput.trim().split('\t').map(Number);

    res.json({
      hasRemote: true,
      hasUpstream: true,
      branch,
      remoteBranch: trackingBranch,
      remoteName,
      ahead: ahead || 0,
      behind: behind || 0,
      isUpToDate: ahead === 0 && behind === 0
    });
  } catch (error) {
    res.json({ error: 'Git operation failed' });
  }
});

// Fetch from remote (using smart remote detection)
router.post('/fetch', async (req, res) => {
  const { project } = req.body;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: currentBranch } = await relayGit(req, 'rev-parse --abbrev-ref HEAD', projectPath);
    const branch = currentBranch.trim();

    let remoteName = 'origin';
    try {
      const { stdout } = await relayGit(req, `rev-parse --abbrev-ref ${branch}@{upstream}`, projectPath);
      remoteName = stdout.trim().split('/')[0];
    } catch (error) {
      // No upstream, try to fetch from origin anyway
    }

    const { stdout } = await relayGit(req, `fetch ${remoteName}`, projectPath, 30000);

    res.json({ success: true, output: stdout || 'Fetch completed successfully', remoteName });
  } catch (error) {
    const msg = error.message || '';
    res.status(500).json({
      error: 'Fetch failed',
      details: msg.includes('Could not resolve hostname')
        ? 'Unable to connect to remote repository. Check your internet connection.'
        : msg.includes('does not appear to be a git repository')
        ? 'No remote repository configured.'
        : 'Failed to fetch from remote'
    });
  }
});

// Pull from remote (fetch + merge using smart remote detection)
router.post('/pull', async (req, res) => {
  const { project } = req.body;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: currentBranch } = await relayGit(req, 'rev-parse --abbrev-ref HEAD', projectPath);
    const branch = currentBranch.trim();

    let remoteName = 'origin';
    let remoteBranch = branch;
    try {
      const { stdout } = await relayGit(req, `rev-parse --abbrev-ref ${branch}@{upstream}`, projectPath);
      const tracking = stdout.trim();
      remoteName = tracking.split('/')[0];
      remoteBranch = tracking.split('/').slice(1).join('/');
    } catch (error) {
      // No upstream, use fallback
    }

    const { stdout } = await relayGit(req, `pull ${remoteName} ${remoteBranch}`, projectPath, 30000);

    res.json({
      success: true,
      output: stdout || 'Pull completed successfully',
      remoteName,
      remoteBranch
    });
  } catch (error) {
    let errorMessage = 'Pull failed';
    let details = 'Operation failed';

    if (error.message.includes('CONFLICT')) {
      errorMessage = 'Merge conflicts detected';
      details = 'Pull created merge conflicts. Please resolve conflicts manually in the editor, then commit the changes.';
    } else if (error.message.includes('Please commit your changes or stash them')) {
      errorMessage = 'Uncommitted changes detected';
      details = 'Please commit or stash your local changes before pulling.';
    } else if (error.message.includes('Could not resolve hostname')) {
      errorMessage = 'Network error';
      details = 'Unable to connect to remote repository. Check your internet connection.';
    } else if (error.message.includes('fatal: \'origin\' does not appear to be a git repository')) {
      errorMessage = 'Remote not configured';
      details = 'No remote repository configured. Add a remote with: git remote add origin <url>';
    } else if (error.message.includes('diverged')) {
      errorMessage = 'Branches have diverged';
      details = 'Your local branch and remote branch have diverged. Consider fetching first to review changes.';
    }

    res.status(500).json({ error: errorMessage, details });
  }
});

// Push commits to remote repository
router.post('/push', async (req, res) => {
  const { project } = req.body;

  if (!project) {
    return res.status(400).json({ error: 'Project name is required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: currentBranch } = await relayGit(req, 'rev-parse --abbrev-ref HEAD', projectPath);
    const branch = currentBranch.trim();

    let remoteName = 'origin';
    let remoteBranch = branch;
    try {
      const { stdout } = await relayGit(req, `rev-parse --abbrev-ref ${branch}@{upstream}`, projectPath);
      const tracking = stdout.trim();
      remoteName = tracking.split('/')[0];
      remoteBranch = tracking.split('/').slice(1).join('/');
    } catch (error) {
      // No upstream, use fallback
    }

    const { stdout } = await relayGit(req, `push ${remoteName} ${remoteBranch}`, projectPath, 30000);

    res.json({
      success: true,
      output: stdout || 'Push completed successfully',
      remoteName,
      remoteBranch
    });
  } catch (error) {
    let errorMessage = 'Push failed';
    let details = 'Operation failed';

    if (error.message.includes('rejected')) {
      errorMessage = 'Push rejected';
      details = 'The remote has newer commits. Pull first to merge changes before pushing.';
    } else if (error.message.includes('non-fast-forward')) {
      errorMessage = 'Non-fast-forward push';
      details = 'Your branch is behind the remote. Pull the latest changes first.';
    } else if (error.message.includes('Could not resolve hostname')) {
      errorMessage = 'Network error';
      details = 'Unable to connect to remote repository. Check your internet connection.';
    } else if (error.message.includes('fatal: \'origin\' does not appear to be a git repository')) {
      errorMessage = 'Remote not configured';
      details = 'No remote repository configured. Add a remote with: git remote add origin <url>';
    } else if (error.message.includes('Permission denied')) {
      errorMessage = 'Authentication failed';
      details = 'Permission denied. Check your credentials or SSH keys.';
    } else if (error.message.includes('no upstream branch')) {
      errorMessage = 'No upstream branch';
      details = 'No upstream branch configured. Use: git push --set-upstream origin <branch>';
    }

    res.status(500).json({ error: errorMessage, details });
  }
});

// Publish branch to remote (set upstream and push)
router.post('/publish', async (req, res) => {
  const { project, branch } = req.body;

  if (!project || !branch) {
    return res.status(400).json({ error: 'Project name and branch are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: currentBranch } = await relayGit(req, 'rev-parse --abbrev-ref HEAD', projectPath);
    const currentBranchName = currentBranch.trim();

    if (currentBranchName !== branch) {
      return res.status(400).json({
        error: `Branch mismatch. Current branch is ${currentBranchName}, but trying to publish ${branch}`
      });
    }

    let remoteName = 'origin';
    try {
      const { stdout } = await relayGit(req, 'remote', projectPath);
      const remotes = stdout.trim().split('\n').filter(r => r.trim());
      if (remotes.length === 0) {
        return res.status(400).json({
          error: 'No remote repository configured. Add a remote with: git remote add origin <url>'
        });
      }
      remoteName = remotes.includes('origin') ? 'origin' : remotes[0];
    } catch (error) {
      return res.status(400).json({
        error: 'No remote repository configured. Add a remote with: git remote add origin <url>'
      });
    }

    const { stdout } = await relayGit(req, `push --set-upstream ${remoteName} ${branch}`, projectPath, 30000);

    res.json({
      success: true,
      output: stdout || 'Branch published successfully',
      remoteName,
      branch
    });
  } catch (error) {
    let errorMessage = 'Publish failed';
    let details = 'Operation failed';

    if (error.message.includes('rejected')) {
      errorMessage = 'Publish rejected';
      details = 'The remote branch already exists and has different commits. Use push instead.';
    } else if (error.message.includes('Could not resolve hostname')) {
      errorMessage = 'Network error';
      details = 'Unable to connect to remote repository. Check your internet connection.';
    } else if (error.message.includes('Permission denied')) {
      errorMessage = 'Authentication failed';
      details = 'Permission denied. Check your credentials or SSH keys.';
    } else if (error.message.includes('fatal:') && error.message.includes('does not appear to be a git repository')) {
      errorMessage = 'Remote not configured';
      details = 'Remote repository not properly configured. Check your remote URL.';
    }

    res.status(500).json({ error: errorMessage, details });
  }
});

// Discard changes for a specific file
router.post('/discard', async (req, res) => {
  const { project, file } = req.body;

  if (!project || !file) {
    return res.status(400).json({ error: 'Project name and file path are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: statusOutput } = await relayGit(req, `status --porcelain "${file}"`, projectPath);

    if (!statusOutput.trim()) {
      return res.status(400).json({ error: 'No changes to discard for this file' });
    }

    const status = statusOutput.substring(0, 2);

    if (status === '??') {
      if (req.isCloud && req.hasRelay()) {
        // Delete untracked file via relay shell command
        await req.sendRelay('shell-command', { command: `rm -rf "${file}"`, cwd: projectPath }, 15000);
      } else {
        const filePath = path.join(projectPath, file);
        const stats = await fs.stat(filePath);
        if (stats.isDirectory()) {
          await fs.rm(filePath, { recursive: true, force: true });
        } else {
          await fs.unlink(filePath);
        }
      }
    } else if (status.includes('M') || status.includes('D')) {
      await relayGit(req, `restore "${file}"`, projectPath);
    } else if (status.includes('A')) {
      await relayGit(req, `reset HEAD "${file}"`, projectPath);
    }

    res.json({ success: true, message: `Changes discarded for ${file}` });
  } catch (error) {
    res.status(500).json({ error: 'Git operation failed' });
  }
});

// Delete untracked file
router.post('/delete-untracked', async (req, res) => {
  const { project, file } = req.body;

  if (!project || !file) {
    return res.status(400).json({ error: 'Project name and file path are required' });
  }

  try {
    const projectPath = await getActualProjectPath(project);

    if (req.isCloud && !req.hasRelay()) {
      return res.status(503).json({ error: 'Machine not connected', message: 'Run "uc connect" on your local machine to use this feature.', code: 'RELAY_NOT_CONNECTED' });
    }

    await validateGitRepo(req, projectPath);

    const { stdout: statusOutput } = await relayGit(req, `status --porcelain "${file}"`, projectPath);

    if (!statusOutput.trim()) {
      return res.status(400).json({ error: 'File is not untracked or does not exist' });
    }

    const status = statusOutput.substring(0, 2);

    if (status !== '??') {
      return res.status(400).json({ error: 'File is not untracked. Use discard for tracked files.' });
    }

    if (req.isCloud && req.hasRelay()) {
      await req.sendRelay('shell-command', { command: `rm -rf "${file}"`, cwd: projectPath }, 15000);
      res.json({ success: true, message: `Untracked file/directory ${file} deleted successfully` });
    } else {
      const filePath = path.join(projectPath, file);
      const stats = await fs.stat(filePath);

      if (stats.isDirectory()) {
        await fs.rm(filePath, { recursive: true, force: true });
        res.json({ success: true, message: `Untracked directory ${file} deleted successfully` });
      } else {
        await fs.unlink(filePath);
        res.json({ success: true, message: `Untracked file ${file} deleted successfully` });
      }
    }
  } catch (error) {
    res.status(500).json({ error: 'Git operation failed' });
  }
});

export default router;
