import * as git from '../lib/git.js'
import * as branch from '../lib/branch.js'
import * as config from '../lib/config.js'
import * as provider from '../lib/provider.js'
import { alignDevelopWithRemote } from '../lib/worktree.js'
import type { MergeResult, MergeOptions } from './types.js'

export async function execute(
  opts: MergeOptions,
  cwd?: string,
): Promise<MergeResult> {
  try {
    // Get current branch
    const currentBranch = await git.getCurrentBranch(cwd)

    // Detect type
    const branchType = branch.detectBranchType(currentBranch)

    // Block if main/develop/other
    if (branchType === 'main' || branchType === 'develop' || branchType === 'other') {
      return {
        success: false,
        strategy: 'merge',
        target: '',
        branchDeleted: false,
        error: `Cannot merge from ${branchType} branch`,
      }
    }

    // Read config
    const cfg = await config.readConfigForPlatform()

    const warnings: string[] = []
    let developAligned: boolean | undefined
    let developSynced: boolean | undefined

    // Determine default strategy based on branch type
    let strategy: 'squash' | 'merge' = 'squash'
    if (branchType === 'release' || branchType === 'hotfix') {
      strategy = 'merge'
    }

    // Override if explicitly requested
    if (opts.merge) strategy = 'merge'
    if (opts.squash) strategy = 'squash'

    // Determine target branch + enforce the hard branch-target invariant.
    const targetBranch = branch.getTargetBranch(branchType, cfg)
    branch.assertBranchTarget(branchType, targetBranch, cfg)

    // Find PR
    const pr = await provider.findPR(currentBranch, cfg.git.provider, cwd)
    if (!pr) {
      return {
        success: false,
        strategy,
        target: targetBranch,
        branchDeleted: false,
        error: `No PR found for branch ${currentBranch}`,
      }
    }

    // Never complete a PR whose target contradicts the branch type (e.g. a
    // feature PR pointing at main) — this would have stopped the feature→main
    // incident at merge time.
    if (pr.target && pr.target !== targetBranch) {
      return {
        success: false,
        prNumber: pr.number,
        strategy,
        target: pr.target,
        branchDeleted: false,
        error: `PR #${pr.number} targets '${pr.target}' but a ${branchType} branch must target '${targetBranch}'. Refusing to merge — abandon or retarget the PR.`,
      }
    }

    // Simon's rule: never integrate a feature onto a STALE local develop. Align
    // the develop worktree with origin FIRST (ff if behind, refuse if diverged).
    if (targetBranch === cfg.git.branches.develop) {
      const dev = cfg.worktrees?.structure?.develop
      if (dev) {
        const align = await alignDevelopWithRemote(dev, cfg.git.branches.develop)
        if (!align.ok) {
          return {
            success: false,
            prNumber: pr.number,
            strategy,
            target: targetBranch,
            branchDeleted: false,
            error: `Pre-merge alignment failed: ${align.error}`,
          }
        }
        developAligned = true
      } else {
        warnings.push('No develop worktree in config — skipped local develop alignment.')
      }
    }

    // Merge PR via provider
    const mergeResult = await provider.mergePR(pr.number, cfg.git.provider, strategy, cwd)
    if (!mergeResult.success) {
      return {
        success: false,
        prNumber: pr.number,
        strategy,
        target: targetBranch,
        branchDeleted: false,
        error: mergeResult.error,
      }
    }

    // Fetch to verify
    await git.fetchAll(cwd)

    // Re-sync local develop with the just-merged origin/develop so it stays aligned
    // (the merge happened on the REMOTE — local develop would otherwise be behind).
    if (targetBranch === cfg.git.branches.develop && cfg.worktrees?.structure?.develop) {
      const after = await alignDevelopWithRemote(cfg.worktrees.structure.develop, cfg.git.branches.develop)
      developSynced = after.ok
      if (!after.ok) warnings.push(`Post-merge re-sync of local develop failed: ${after.error}`)
    }

    // Check if branch was deleted by provider
    const stillExists = await git.branchExists(currentBranch, true, cwd)

    return {
      success: true,
      prNumber: pr.number,
      strategy,
      target: targetBranch,
      branchDeleted: !stillExists,
      developAligned,
      developSynced,
      warnings: warnings.length ? warnings : undefined,
    }
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err)
    return {
      success: false,
      strategy: 'merge',
      target: '',
      branchDeleted: false,
      error: message,
    }
  }
}
