/**
 * Adopt Branch Use Case
 *
 * Imports an existing git branch into Shep's feature tracking system.
 * Creates a worktree (if needed), derives feature metadata from the branch name,
 * and persists a Feature entity. Branches with an open PR get lifecycle=Review
 * (shown as "REVIEW" in the UI); all others get lifecycle=Maintain (completed).
 *
 * This is a standalone use case — it does NOT extend or modify CreateFeatureUseCase.
 * It needs only three dependencies: IFeatureRepository, IRepositoryRepository, and IWorktreeService.
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { injectable, inject } from 'tsyringe';
import { randomUUID } from 'node:crypto';
import { SdlcLifecycle, PrStatus, BuildMode } from '../../../domain/generated/output.js';
import { deriveName, deriveSlug } from './branch-name-utils.js';
/** Branches that must not be adopted as features. */
const PROTECTED_BRANCHES = new Set(['main', 'master']);
let AdoptBranchUseCase = class AdoptBranchUseCase {
    featureRepo;
    repositoryRepo;
    worktreeService;
    gitPrService;
    constructor(featureRepo, repositoryRepo, worktreeService, gitPrService) {
        this.featureRepo = featureRepo;
        this.repositoryRepo = repositoryRepo;
        this.worktreeService = worktreeService;
        this.gitPrService = gitPrService;
    }
    async execute(input) {
        const { branchName, repositoryPath } = input;
        // --- Guard: reject main/master ---
        if (PROTECTED_BRANCHES.has(branchName)) {
            throw new Error(`Cannot adopt the "${branchName}" branch. The main branch should not be tracked as a feature.`);
        }
        // --- Guard: duplicate adoption ---
        const existing = await this.featureRepo.findByBranch(branchName, repositoryPath);
        if (existing) {
            throw new Error(`Branch "${branchName}" is already tracked as feature "${existing.name}" (${existing.id}). ` +
                'A branch can only be adopted once per repository.');
        }
        // --- Derive slug and check uniqueness ---
        const slug = deriveSlug(branchName);
        const slugCollision = await this.featureRepo.findBySlug(slug, repositoryPath);
        if (slugCollision) {
            throw new Error(`A feature with slug "${slug}" already exists in this repository (feature "${slugCollision.name}"). ` +
                `Cannot adopt branch "${branchName}" because its derived slug collides.`);
        }
        // --- Validate branch existence (local or remote) ---
        const branchRef = await this.resolveBranchRef(branchName, repositoryPath);
        // --- Resolve or create repository entity ---
        const repository = await this.resolveRepository(repositoryPath);
        // --- Create or reuse worktree ---
        const worktreePath = this.worktreeService.getWorktreePath(repositoryPath, slug);
        const worktreeExists = await this.worktreeService.exists(repositoryPath, branchName);
        if (!worktreeExists) {
            await this.worktreeService.addExisting(repositoryPath, branchRef, worktreePath);
        }
        // --- Detect open PR for this branch ---
        const prData = await this.detectPrForBranch(branchName, repositoryPath);
        // --- Derive feature metadata ---
        const name = deriveName(branchName);
        const now = new Date();
        const hasOpenPr = prData !== undefined;
        // If the branch has an open (not yet merged) PR, place the feature in
        // Review so the node shows "REVIEW" instead of "COMPLETED". Only truly
        // finished branches (no PR, merged PR, or closed PR) get Maintain.
        const lifecycle = prData?.status === PrStatus.Open ? SdlcLifecycle.Review : SdlcLifecycle.Maintain;
        // --- Persist feature ---
        const feature = {
            id: randomUUID(),
            name,
            slug,
            description: '',
            userQuery: '',
            repositoryPath,
            branch: branchName,
            lifecycle,
            messages: [],
            relatedArtifacts: [],
            buildMode: BuildMode.Application,
            fast: false,
            push: false,
            openPr: hasOpenPr,
            forkAndPr: false,
            commitSpecs: true,
            ciWatchEnabled: true,
            enableEvidence: false,
            injectSkills: false,
            commitEvidence: false,
            approvalGates: {
                allowPrd: false,
                allowPlan: false,
                allowMerge: false,
            },
            worktreePath,
            repositoryId: repository.id,
            pr: prData,
            bedrockEnabled: false,
            createdAt: now,
            updatedAt: now,
        };
        await this.featureRepo.create(feature);
        return { feature };
    }
    /**
     * Resolve the git ref to use for worktree creation.
     * Returns the branch name for local branches, or "origin/<branch>" for remote-only.
     * Throws if branch does not exist anywhere.
     */
    async resolveBranchRef(branchName, repositoryPath) {
        const localExists = await this.worktreeService.branchExists(repositoryPath, branchName);
        if (localExists) {
            return branchName;
        }
        const remoteExists = await this.worktreeService.remoteBranchExists(repositoryPath, branchName);
        if (remoteExists) {
            return `origin/${branchName}`;
        }
        throw new Error(`Branch "${branchName}" does not exist locally or on the remote. ` +
            'Check the branch name spelling and ensure you have fetched from the remote.');
    }
    /**
     * Detect if the branch has an open PR (or one whose closure we cannot confirm).
     * Returns PullRequest data if found, undefined otherwise.
     * Gracefully returns undefined if gh CLI is unavailable or the repo has no remote.
     */
    async detectPrForBranch(branchName, repositoryPath) {
        try {
            const hasRemote = await this.gitPrService.hasRemote(repositoryPath);
            if (!hasRemote)
                return undefined;
            const prStatuses = await this.gitPrService.listPrStatuses(repositoryPath);
            const match = prStatuses.find((pr) => pr.headRefName === branchName);
            if (!match)
                return undefined;
            // If the PR is definitively closed (not merged), skip it
            if (match.state === PrStatus.Closed)
                return undefined;
            // Open or Merged — attach PR data
            return {
                url: match.url,
                number: match.number,
                status: match.state,
                mergeable: match.mergeable,
            };
        }
        catch {
            // gh CLI not installed, auth failure, etc. — gracefully skip PR detection
            return undefined;
        }
    }
    /**
     * Find or create a Repository entity for the given path.
     * Mirrors the pattern from CreateFeatureUseCase.
     */
    async resolveRepository(repositoryPath) {
        const normalizedPath = repositoryPath.replace(/\\/g, '/').replace(/\/+$/, '') || repositoryPath;
        let repository = await this.repositoryRepo.findByPath(normalizedPath);
        if (!repository) {
            const now = new Date();
            const repoName = normalizedPath.split('/').pop() ?? normalizedPath;
            repository = await this.repositoryRepo.create({
                id: randomUUID(),
                name: repoName,
                path: normalizedPath,
                bedrockEnabled: false,
                createdAt: now,
                updatedAt: now,
            });
            if (!repository) {
                throw new Error(`Failed to create or retrieve repository record for path: ${normalizedPath}`);
            }
        }
        return repository;
    }
};
AdoptBranchUseCase = __decorate([
    injectable(),
    __param(0, inject('IFeatureRepository')),
    __param(1, inject('IRepositoryRepository')),
    __param(2, inject('IWorktreeService')),
    __param(3, inject('IGitPrService')),
    __metadata("design:paramtypes", [Object, Object, Object, Object])
], AdoptBranchUseCase);
export { AdoptBranchUseCase };
