/**
 * Git Tools - Version control operations
 *
 * Implements T-208: git.status, git.diff, git.commit, git.branch, git.merge
 * .git/ internals protected (DD §18.5).
 *
 * @module packages/runtime/src/tools/git
 */

import { execSync } from 'child_process'
import { existsSync } from 'fs'
import { join, dirname } from 'path'
import type { ToolDefinition, ToolCall, ToolResultEnvelope, ISOTimeString } from '@aircoding/contracts'

// Git tools definitions
export const git_status: ToolDefinition = {
  name: 'git.status',
  category: 'vcs',
  description: 'Show working tree status',
  input_schema: {
    type: 'object',
    properties: {
      path: { type: 'string', description: 'Repository path (default: project root)' }
    }
  },
  permissions: { read: true, write: false, network: false },
  streaming: false
}

export const git_diff: ToolDefinition = {
  name: 'git.diff',
  category: 'vcs',
  description: 'Show changes',
  input_schema: {
    type: 'object',
    properties: {
      path: { type: 'string', description: 'Repository path' },
      staged: { type: 'boolean', default: false, description: 'Show staged changes' },
      range: { type: 'string', description: 'Commit range (e.g., HEAD~3..HEAD)' }
    }
  },
  permissions: { read: true, write: false, network: false },
  streaming: false
}

export const git_commit: ToolDefinition = {
  name: 'git.commit',
  category: 'vcs',
  description: 'Create a commit',
  input_schema: {
    type: 'object',
    properties: {
      path: { type: 'string', description: 'Repository path' },
      message: { type: 'string', description: 'Commit message' },
      all: { type: 'boolean', default: false, description: 'Stage all changes' },
      amend: { type: 'boolean', default: false, description: 'Amend last commit' }
    },
    required: ['message']
  },
  permissions: { read: false, write: true, network: false },
  streaming: false
}

export const git_branch: ToolDefinition = {
  name: 'git.branch',
  category: 'vcs',
  description: 'List, create, or delete branches',
  input_schema: {
    type: 'object',
    properties: {
      path: { type: 'string', description: 'Repository path' },
      list: { type: 'boolean', default: true, description: 'List branches' },
      create: { type: 'string', description: 'Create new branch' },
      delete: { type: 'string', description: 'Delete branch' },
      current: { type: 'boolean', default: false, description: 'Show current branch' }
    }
  },
  permissions: { read: true, write: true, network: false },
  streaming: false
}

export const git_merge: ToolDefinition = {
  name: 'git.merge',
  category: 'vcs',
  description: 'Merge branches',
  input_schema: {
    type: 'object',
    properties: {
      path: { type: 'string', description: 'Repository path' },
      branch: { type: 'string', description: 'Branch to merge' },
      no_ff: { type: 'boolean', default: true, description: 'No fast-forward merge' },
      message: { type: 'string', description: 'Merge commit message' }
    },
    required: ['branch']
  },
  permissions: { read: false, write: true, network: false },
  streaming: false
}

// Executor
export function createGitExecutor(project_root: string) {
  const resolve_repo = (path?: string): string => {
    const dir = path || project_root
    if (!existsSync(join(dir, '.git'))) {
      throw new Error('Not a git repository')
    }
    return dir
  }

  const run_git = (repo_path: string, ...args: string[]): string => {
    try {
      return execSync(`git ${args.join(' ')}`, {
        cwd: repo_path,
        encoding: 'utf-8',
        stdio: ['pipe', 'pipe', 'pipe']
      })
    } catch (error) {
      const err = error as { message?: string; status?: number }
      throw new Error(err.message || `git failed with code ${err.status}`)
    }
  }

  return {
    'git.status': async (call: ToolCall): Promise<ToolResultEnvelope> => {
      const { path } = call.arguments as { path?: string }
      try {
        const repo = resolve_repo(path)
        const output = run_git(repo, 'status', '--porcelain')
        return create_result(call.id, 'git.status', 'text', { status: output || 'clean', raw: output })
      } catch (error) {
        return create_result(call.id, 'git.status', 'error', { message: error instanceof Error ? error.message : String(error) })
      }
    },

    'git.diff': async (call: ToolCall): Promise<ToolResultEnvelope> => {
      const { path, staged, range } = call.arguments as { path?: string; staged?: boolean; range?: string }
      try {
        const repo = resolve_repo(path)
        let args = ['diff']
        if (staged) args.push('--staged')
        if (range) args.push(range)
        const output = run_git(repo, ...args)
        return create_result(call.id, 'git.diff', 'text', { diff: output || 'no changes', lines: output.split('\n').length })
      } catch (error) {
        return create_result(call.id, 'git.diff', 'error', { message: error instanceof Error ? error.message : String(error) })
      }
    },

    'git.commit': async (call: ToolCall): Promise<ToolResultEnvelope> => {
      const { path, message, all, amend } = call.arguments as { path?: string; message: string; all?: boolean; amend?: boolean }
      try {
        const repo = resolve_repo(path)
        const args = ['commit']
        if (all) args.push('-a')
        if (amend) args.push('--amend')
        args.push('-m', message)
        const output = run_git(repo, ...args)
        return create_result(call.id, 'git.commit', 'text', { message: 'committed', output })
      } catch (error) {
        return create_result(call.id, 'git.commit', 'error', { message: error instanceof Error ? error.message : String(error) })
      }
    },

    'git.branch': async (call: ToolCall): Promise<ToolResultEnvelope> => {
      const { path, list, create, delete: deleteBranch, current } = call.arguments as {
        path?: string
        list?: boolean
        create?: string
        delete?: string
        current?: boolean
      }
      try {
        const repo = resolve_repo(path)
        let output = ''

        if (current) {
          output = run_git(repo, 'branch', '--show-current')
        } else if (create) {
          run_git(repo, 'branch', create)
          output = `Created branch: ${create}`
        } else if (deleteBranch) {
          run_git(repo, 'branch', '-d', deleteBranch)
          output = `Deleted branch: ${deleteBranch}`
        } else {
          output = run_git(repo, 'branch', '-a')
        }

        return create_result(call.id, 'git.branch', 'text', { output: output.trim() })
      } catch (error) {
        return create_result(call.id, 'git.branch', 'error', { message: error instanceof Error ? error.message : String(error) })
      }
    },

    'git.merge': async (call: ToolCall): Promise<ToolResultEnvelope> => {
      const { path, branch, no_ff, message } = call.arguments as {
        path?: string
        branch: string
        no_ff?: boolean
        message?: string
      }
      try {
        const repo = resolve_repo(path)
        const args = ['merge']
        if (no_ff) args.push('--no-ff')
        if (message) args.push('-m', message)
        args.push(branch)
        const output = run_git(repo, ...args)
        return create_result(call.id, 'git.merge', 'text', { merged: branch, output })
      } catch (error) {
        return create_result(call.id, 'git.merge', 'error', { message: error instanceof Error ? error.message : String(error) })
      }
    }
  }
}

function create_result(call_id: string, tool_name: string, type: 'text' | 'error', content: Record<string, unknown>): ToolResultEnvelope {
  return {
    call_id,
    tool_name,
    type,
    content,
    metadata: { timestamp: new Date().toISOString() as ISOTimeString }
  }
}