"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionService = void 0;
const logger_1 = require("../utils/logger");
const CommitMessageGenerator_1 = require("./CommitMessageGenerator");
const LocalGitService_1 = require("./LocalGitService");
const PRTitleGenerator_1 = require("./PRTitleGenerator");
const log = (0, logger_1.createLogger)('SessionService');
class SessionService {
    gitService;
    projectPath;
    branchName;
    repoInfo;
    isGitEnabled;
    baseBranch;
    startingSha;
    lastKnownPrUrl;
    constructor(projectPath, isGitEnabled = false, baseBranch = 'main') {
        this.gitService = new LocalGitService_1.LocalGitService();
        this.projectPath = projectPath;
        this.isGitEnabled = isGitEnabled;
        this.baseBranch = baseBranch;
    }
    /** Detects repo info and current branch without creating a new one. */
    async initializeSession() {
        if (!this.isGitEnabled) {
            log.info('Git tracking disabled, skipping session initialization');
            return;
        }
        try {
            this.repoInfo = await this.gitService.getRepositoryInfo(this.projectPath);
            log.info(`Repository detected: ${this.repoInfo.owner}/${this.repoInfo.repo}`);
            await this.gitService.configureDefaultIdentity(this.projectPath);
            // Use whatever branch the user is already on
            this.branchName = await this.gitService.getCurrentBranch(this.projectPath);
            // Determine the best base branch for diffing
            this.baseBranch = await this.determineBaseBranch(this.branchName);
            this.startingSha = await this.gitService.getHeadSha(this.projectPath);
            // Check for existing PR
            this.lastKnownPrUrl = await this.gitService.getExistingPrUrl(this.projectPath, this.branchName);
            log.info(`Git session initialized on branch: ${this.branchName} (base: ${this.baseBranch})`);
        }
        catch (error) {
            log.warn('Failed to initialize git session:', error);
            this.isGitEnabled = false;
            throw error;
        }
    }
    async commitModification(change) {
        if (!this.isGitEnabled || !this.branchName) {
            log.debug('Git tracking disabled or not initialized, skipping commit');
            return;
        }
        try {
            // Get the diff before staging so the LLM can see what changed
            const diff = await this.gitService.getUnstagedDiff(this.projectPath);
            const filesChanged = change.files.map((f) => f.filePath);
            const commitMessage = await (0, CommitMessageGenerator_1.generateCommitMessage)({
                diff,
                instruction: change.description,
                filesChanged,
            });
            const commitSha = await this.gitService.commitChange(this.projectPath, change, undefined, commitMessage);
            log.info(`Successfully committed modification with SHA: ${commitSha} on branch: ${this.branchName}`);
        }
        catch (error) {
            log.error(`Failed to commit modification on branch ${this.branchName}:`, error);
            throw error;
        }
    }
    async publishChanges() {
        return this.gitService.executeInLock(this.projectPath, async () => {
            if (!this.isGitEnabled || !this.branchName) {
                throw new Error('Git tracking not enabled or branch not created');
            }
            try {
                // Gather context for PR title and description generation.
                // getDiff is best-effort — a failure should not block publishing.
                const [commitMessages, diffStats, diff] = await Promise.all([
                    this.gitService.getCommitLog(this.projectPath, this.baseBranch),
                    this.gitService.getDiffStats(this.projectPath, this.baseBranch),
                    this.gitService
                        .getDiff(this.projectPath, this.baseBranch, 'HEAD')
                        .catch((err) => {
                        log.warn('Failed to fetch diff for PR description:', err);
                        return '';
                    }),
                ]);
                const filesChanged = diffStats.files.map((f) => f.file);
                const titleContext = {
                    commitMessages,
                    filesChanged,
                    branchName: this.branchName,
                };
                const [title, description] = await Promise.all([
                    (0, PRTitleGenerator_1.generatePRTitle)(titleContext),
                    (0, PRTitleGenerator_1.generatePRDescription)({ ...titleContext, diff }),
                ]);
                // Push the branch to origin
                await this.gitService.pushBranch(this.projectPath, this.branchName);
                log.info(`Pushed branch ${this.branchName} to origin`);
                // Create the pull request via GitHub CLI
                const prUrl = await this.gitService.createPullRequest(this.projectPath, this.branchName, title, description, this.baseBranch);
                log.info(`Created pull request for branch ${this.branchName}: ${prUrl}`);
                this.lastKnownPrUrl = prUrl;
                return { prUrl, branchName: this.branchName };
            }
            catch (error) {
                log.error('Failed to publish changes:', error);
                throw error;
            }
        });
    }
    /** Updates the tracked branch after the user explicitly creates one. */
    async switchBranch(newBranchName, parentBranch) {
        return this.gitService.executeInLock(this.projectPath, async () => {
            this.branchName = newBranchName;
            this.baseBranch = parentBranch;
            this.startingSha = await this.gitService.getHeadSha(this.projectPath);
            this.lastKnownPrUrl = await this.gitService.getExistingPrUrl(this.projectPath, this.branchName);
            log.info(`Switched to branch: ${newBranchName} (base: ${parentBranch})`);
        });
    }
    /** Switches to an existing local branch. */
    async switchToBranch(branchName) {
        return this.gitService.executeInLock(this.projectPath, async () => {
            await this.gitService.checkoutBranch(this.projectPath, branchName);
            this.branchName = branchName;
            // When switching to an existing branch, reset the base branch to the default
            // rather than the previous branch (parentBranch).
            this.baseBranch = await this.determineBaseBranch(branchName);
            this.startingSha = await this.gitService.getHeadSha(this.projectPath);
            this.lastKnownPrUrl = await this.gitService.getExistingPrUrl(this.projectPath, this.branchName);
            log.info(`Session updated: Switched to branch ${branchName}`);
        });
    }
    /** Lists all local branches. */
    async listBranches() {
        return this.gitService.listBranches(this.projectPath);
    }
    getBaseBranch() {
        return this.baseBranch;
    }
    async getSessionStatus() {
        return this.gitService.executeInLock(this.projectPath, async () => {
            if (!this.isGitEnabled) {
                return {
                    isGitEnabled: false,
                    hasUncommittedChanges: false,
                    canUndo: false,
                };
            }
            try {
                // Single git status call provides both hasChanges and the current branch
                const gitStatus = await this.gitService.getStatus(this.projectPath);
                // Pass the already-known branch to skip a redundant rev-parse subprocess
                const canUndo = this.startingSha
                    ? await this.gitService.canUndo(this.projectPath, this.branchName, this.startingSha, gitStatus.branch)
                    : false;
                return {
                    isGitEnabled: this.isGitEnabled,
                    branchName: this.branchName,
                    baseBranch: this.baseBranch,
                    hasUncommittedChanges: gitStatus.hasChanges,
                    repoInfo: this.repoInfo,
                    canUndo,
                    prUrl: this.lastKnownPrUrl,
                    diffStats: await this.gitService.getDiffStats(this.projectPath, this.baseBranch),
                };
            }
            catch (error) {
                log.warn('Failed to get session status:', error);
                return {
                    isGitEnabled: this.isGitEnabled,
                    branchName: this.branchName,
                    baseBranch: this.baseBranch,
                    hasUncommittedChanges: false,
                    repoInfo: this.repoInfo,
                    canUndo: false,
                };
            }
        });
    }
    async undoLastChange() {
        if (!this.isGitEnabled || !this.branchName) {
            throw new Error('Git tracking not enabled or branch not created');
        }
        const canUndo = this.startingSha
            ? await this.gitService.canUndo(this.projectPath, this.branchName, this.startingSha)
            : false;
        if (!canUndo) {
            throw new Error('No changes to undo on the current branch');
        }
        try {
            await this.gitService.undoLastCommit(this.projectPath);
            log.info(`Undid last change on branch: ${this.branchName}`);
        }
        catch (error) {
            log.error('Failed to undo last change:', error);
            throw error;
        }
    }
    async determineBaseBranch(currentBranchName) {
        const originDefault = await this.gitService.getOriginDefaultBranch(this.projectPath);
        // e.g. "origin/main" -> "main"
        const localDefault = originDefault.replace(/^origin\//, '');
        // If we are on the default branch (e.g. main), compare against origin/main
        // so we can see local commits vs remote.
        if (currentBranchName === localDefault) {
            return originDefault;
        }
        // For all other branches, compare against the default branch (e.g. main)
        // This assumes a flat branching model (feature branches off main).
        return localDefault;
    }
}
exports.SessionService = SessionService;
//# sourceMappingURL=SessionService.js.map