import { existsSync } from 'fs'
import { basename } from 'path'
import { createGitService, isAutoNamedBranch } from './git-service'
import { type BreedType } from './breed-names'
import { normalizeWorktreePath } from './path-utils'
import { scriptRunner } from './script-runner'
import { assignPort, releasePort } from './port-registry'
import { createLogger } from './logger'
import type { DatabaseService } from '../db/database'
import { APP_SETTINGS_DB_KEY } from '@shared/types/settings'

const log = createLogger({ component: 'WorktreeOps' })

// ── Parameter types ─────────────────────────────────────────────

export interface CreateWorktreeParams {
  projectId: string
  projectPath: string
  projectName: string
}

export interface DeleteWorktreeParams {
  worktreeId: string
  worktreePath: string
  branchName: string
  projectPath: string
  archive: boolean // true = Archive (delete branch), false = Unbranch (keep branch)
}

export interface SyncWorktreesParams {
  projectId: string
  projectPath: string
}

export interface DuplicateWorktreeParams {
  projectId: string
  projectPath: string
  projectName: string
  sourceBranch: string
  sourceWorktreePath: string
}

export interface RenameBranchParams {
  worktreeId: string
  worktreePath: string
  oldBranch: string
  newBranch: string
}

export interface CreateFromBranchParams {
  projectId: string
  projectPath: string
  projectName: string
  branchName: string
  prNumber?: number
}

// ── Result types ────────────────────────────────────────────────

export interface WorktreeResult {
  success: boolean
  worktree?: {
    id: string
    project_id: string
    name: string
    branch_name: string
    path: string
    status: string
    created_at: string
    last_accessed_at: string
  }
  pullInfo?: { success: boolean; updated: boolean; commits?: number }
  error?: string
}

export interface SimpleResult {
  success: boolean
  error?: string
}

function getImportedWorktreeName(branch: string, worktreePath: string): string {
  return branch || basename(worktreePath)
}

// ── Helpers ─────────────────────────────────────────────────────

export function getBreedType(db: DatabaseService): BreedType {
  try {
    const settingsJson = db.getSetting(APP_SETTINGS_DB_KEY)
    if (settingsJson) {
      const settings = JSON.parse(settingsJson)
      if (settings.breedType === 'cats') {
        return 'cats'
      }
    }
  } catch {
    // Fall back to dogs
  }
  return 'dogs'
}

function getAutoPullSetting(db: DatabaseService): boolean {
  try {
    const settingsJson = db.getSetting(APP_SETTINGS_DB_KEY)
    if (settingsJson) {
      const settings = JSON.parse(settingsJson)
      if (typeof settings.autoPullBeforeWorktree === 'boolean') {
        return settings.autoPullBeforeWorktree
      }
    }
  } catch {
    // Fall back to default
  }
  return true // Default: enabled
}

// ── Operations ──────────────────────────────────────────────────

export async function createWorktreeOp(
  db: DatabaseService,
  params: CreateWorktreeParams
): Promise<WorktreeResult> {
  log.info('Creating worktree', {
    projectName: params.projectName,
    projectId: params.projectId
  })
  try {
    const gitService = createGitService(params.projectPath)

    // Read breed type preference from settings
    const breedType = getBreedType(db)
    const autoPull = getAutoPullSetting(db)

    const result = await gitService.createWorktree(params.projectName, breedType, { autoPull })

    if (!result.success || !result.name || !result.path || !result.branchName) {
      log.warn('Worktree creation failed', {
        error: result.error,
        projectName: params.projectName
      })
      return {
        success: false,
        error: result.error || 'Failed to create worktree'
      }
    }

    // Create database entry
    const worktree = db.createWorktree({
      project_id: params.projectId,
      name: result.name,
      branch_name: result.branchName,
      path: result.path
    })

    // Auto-assign port if project has it enabled
    const project = db.getProject(params.projectId)
    if (project && project.auto_assign_port) {
      const port = assignPort(worktree.path)
      log.info('Auto-assigned port to new worktree', {
        worktreeId: worktree.id,
        path: worktree.path,
        port
      })
    }

    log.info('Worktree created successfully', { name: result.name, path: result.path })
    return {
      success: true,
      worktree,
      pullInfo: result.pullInfo
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error'
    log.error('Worktree creation error', error instanceof Error ? error : new Error(message), {
      params
    })
    return {
      success: false,
      error: message
    }
  }
}

export async function deleteWorktreeOp(
  db: DatabaseService,
  params: DeleteWorktreeParams
): Promise<SimpleResult> {
  try {
    // Guard: block delete/archive of default worktrees
    const worktree = db.getWorktree(params.worktreeId)
    if (worktree?.is_default) {
      return {
        success: false,
        error: 'Cannot archive or delete the default worktree'
      }
    }

    // Run archive script if configured (before git operations)
    const project = worktree?.project_id ? db.getProject(worktree.project_id) : null
    if (project?.archive_script) {
      // Pass raw script lines -- scriptRunner.parseCommands handles splitting/filtering
      const commands = [project.archive_script]
      log.info('Running archive script before worktree deletion', {
        worktreeId: params.worktreeId
      })
      const scriptResult = await scriptRunner.runAndWait(commands, params.worktreePath, 30000)
      if (scriptResult.success) {
        log.info('Archive script completed successfully', { output: scriptResult.output })
      } else {
        log.warn('Archive script failed, proceeding with archival anyway', {
          error: scriptResult.error,
          output: scriptResult.output
        })
      }
    }

    const gitService = createGitService(params.projectPath)

    let result
    if (params.archive) {
      // Archive: remove worktree AND delete branch
      result = await gitService.archiveWorktree(params.worktreePath, params.branchName)
    } else {
      // Unbranch: remove worktree but keep branch
      result = await gitService.removeWorktree(params.worktreePath)
    }

    if (!result.success) {
      return result
    }

    // Release any assigned port for this worktree
    releasePort(params.worktreePath)

    // Update database - archive the worktree record
    db.archiveWorktree(params.worktreeId)

    return { success: true }
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error'
    return {
      success: false,
      error: message
    }
  }
}

export async function syncWorktreesOp(
  db: DatabaseService,
  params: SyncWorktreesParams
): Promise<SimpleResult> {
  try {
    const gitService = createGitService(params.projectPath)
    const normalizedProjectPath = normalizeWorktreePath(params.projectPath)
    const project = db.getProject(params.projectId)

    // Get actual worktrees from git
    const gitWorktrees = await gitService.listWorktrees()
    const normalizedGitWorktrees = gitWorktrees.map((worktree) => ({
      ...worktree,
      normalizedPath: normalizeWorktreePath(worktree.path)
    }))
    const gitWorktreePaths = new Set(normalizedGitWorktrees.map((w) => w.normalizedPath))

    // Get database worktrees
    const dbWorktrees = db.getActiveWorktreesByProject(params.projectId)
    const dbWorktreePaths = new Set(dbWorktrees.map((w) => normalizeWorktreePath(w.path)))

    for (const gitWorktree of normalizedGitWorktrees) {
      if (
        gitWorktree.normalizedPath === normalizedProjectPath ||
        dbWorktreePaths.has(gitWorktree.normalizedPath)
      ) {
        continue
      }

      if (!existsSync(gitWorktree.path)) {
        log.info('Skipping missing git worktree during sync', {
          projectId: params.projectId,
          path: gitWorktree.path,
          branch: gitWorktree.branch
        })
        continue
      }

      const importedName = getImportedWorktreeName(gitWorktree.branch, gitWorktree.path)

      log.info('Importing git worktree into database', {
        projectId: params.projectId,
        path: gitWorktree.path,
        branch: gitWorktree.branch,
        name: importedName
      })

      const importedWorktree = db.createWorktree({
        project_id: params.projectId,
        name: importedName,
        branch_name: gitWorktree.branch,
        path: gitWorktree.path
      })

      if (project?.auto_assign_port) {
        const port = assignPort(importedWorktree.path)
        log.info('Auto-assigned port to imported worktree', {
          worktreeId: importedWorktree.id,
          path: importedWorktree.path,
          port
        })
      }
    }

    // Build a map of git worktree path -> branch for quick lookup
    const gitBranchByPath = new Map(normalizedGitWorktrees.map((w) => [w.normalizedPath, w.branch]))

    // Check each database worktree
    for (const dbWorktree of dbWorktrees) {
      // If worktree path doesn't exist in git worktrees or on disk
      const normalizedDbWorktreePath = normalizeWorktreePath(dbWorktree.path)

      if (!gitWorktreePaths.has(normalizedDbWorktreePath) && !existsSync(dbWorktree.path)) {
        if (dbWorktree.is_default) {
          continue
        }

        // Mark as archived (worktree was removed outside of Hive)
        db.archiveWorktree(dbWorktree.id)
        continue
      }

      // Sync branch name if it was renamed outside of Hive
      const gitBranch = gitBranchByPath.get(normalizedDbWorktreePath)
      if (
        gitBranch !== undefined &&
        gitBranch !== dbWorktree.branch_name &&
        !dbWorktree.branch_renamed
      ) {
        log.info('Branch renamed externally, updating DB', {
          worktreeId: dbWorktree.id,
          oldBranch: dbWorktree.branch_name,
          newBranch: gitBranch
        })
        // Update branch_name always. Also update display name if it still matches
        // the old branch name OR is a city placeholder name (never meaningfully customized).
        const nameMatchesBranch = dbWorktree.name === dbWorktree.branch_name
        const worktreeName = dbWorktree.name.toLowerCase()
        const isAutoName = isAutoNamedBranch(worktreeName)
        const shouldUpdateName = nameMatchesBranch || isAutoName
        const syncedName = getImportedWorktreeName(gitBranch, dbWorktree.path)
        db.updateWorktree(dbWorktree.id, {
          branch_name: gitBranch,
          ...(shouldUpdateName ? { name: syncedName } : {})
        })
      }
    }

    // Prune any stale git worktree entries
    await gitService.pruneWorktrees()

    return { success: true }
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error'
    return {
      success: false,
      error: message
    }
  }
}

export async function duplicateWorktreeOp(
  db: DatabaseService,
  params: DuplicateWorktreeParams
): Promise<WorktreeResult> {
  log.info('Duplicating worktree', {
    sourceBranch: params.sourceBranch,
    projectName: params.projectName
  })

  if (!params.sourceBranch) {
    return {
      success: false,
      error: 'Detached HEAD worktrees cannot be duplicated'
    }
  }

  try {
    const gitService = createGitService(params.projectPath)
    const result = await gitService.duplicateWorktree(
      params.sourceBranch,
      params.sourceWorktreePath,
      params.projectName
    )

    if (!result.success || !result.name || !result.path || !result.branchName) {
      log.warn('Worktree duplication failed', { error: result.error })
      return {
        success: false,
        error: result.error || 'Failed to duplicate worktree'
      }
    }

    // Create database entry
    const worktree = db.createWorktree({
      project_id: params.projectId,
      name: result.name,
      branch_name: result.branchName,
      path: result.path
    })

    // Copy context from source worktree
    const sourceWorktree = db.getWorktreeByPath(params.sourceWorktreePath)
    if (sourceWorktree?.context) {
      db.updateWorktreeContext(worktree.id, sourceWorktree.context)
    }

    // Auto-assign port if project has it enabled
    const project = db.getProject(params.projectId)
    if (project && project.auto_assign_port) {
      const port = assignPort(worktree.path)
      log.info('Auto-assigned port to duplicated worktree', {
        worktreeId: worktree.id,
        path: worktree.path,
        port
      })
    }

    log.info('Worktree duplicated successfully', { name: result.name, path: result.path })
    return {
      success: true,
      worktree
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error'
    log.error('Worktree duplication error', error instanceof Error ? error : new Error(message), {
      params
    })
    return {
      success: false,
      error: message
    }
  }
}

export async function renameWorktreeBranchOp(
  db: DatabaseService,
  params: RenameBranchParams
): Promise<SimpleResult> {
  log.info('Renaming worktree branch', {
    worktreePath: params.worktreePath,
    oldBranch: params.oldBranch,
    newBranch: params.newBranch
  })

  if (!params.oldBranch) {
    return {
      success: false,
      error: 'Detached HEAD worktrees cannot be renamed'
    }
  }

  try {
    const gitService = createGitService(params.worktreePath)
    const result = await gitService.renameBranch(
      params.worktreePath,
      params.oldBranch,
      params.newBranch
    )
    if (result.success) {
      db.updateWorktree(params.worktreeId, {
        branch_name: params.newBranch,
        branch_renamed: 1
      })
    }
    return result
  } catch (error) {
    log.error(
      'Rename worktree branch failed',
      error instanceof Error ? error : new Error('Unknown error')
    )
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    }
  }
}

export async function createWorktreeFromBranchOp(
  db: DatabaseService,
  params: CreateFromBranchParams
): Promise<WorktreeResult> {
  log.info('Creating worktree from branch', {
    projectName: params.projectName,
    branchName: params.branchName
  })
  try {
    // Read breed type preference from settings
    const breedType = getBreedType(db)
    const autoPull = getAutoPullSetting(db)

    const gitService = createGitService(params.projectPath)
    const result = await gitService.createWorktreeFromBranch(
      params.projectName,
      params.branchName,
      breedType,
      params.prNumber,
      { autoPull }
    )
    if (!result.success || !result.path) {
      return { success: false, error: result.error || 'Failed to create worktree from branch' }
    }
    const worktree = db.createWorktree({
      project_id: params.projectId,
      name: result.name || params.branchName,
      branch_name: result.branchName || params.branchName,
      path: result.path
    })

    // Auto-assign port if project has it enabled
    const project = db.getProject(params.projectId)
    if (project && project.auto_assign_port) {
      const port = assignPort(worktree.path)
      log.info('Auto-assigned port to worktree from branch', {
        worktreeId: worktree.id,
        path: worktree.path,
        port
      })
    }

    return { success: true, worktree, pullInfo: result.pullInfo }
  } catch (error) {
    log.error(
      'Create worktree from branch failed',
      error instanceof Error ? error : new Error('Unknown error')
    )
    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    }
  }
}
