"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createScopedTicketTrackerClient = createScopedTicketTrackerClient;
const issue_normalization_1 = require("./issue-normalization");
const promise_cache_1 = require("../promise-cache");
function createScopedTicketTrackerClient(baseClient) {
    return new DefaultScopedTicketTrackerClient(baseClient);
}
class DefaultScopedTicketTrackerClient {
    baseClient;
    issueListCache = new Map();
    issueRecordListCache = new Map();
    issueRecordSearchCache = new Map();
    issueCache = new Map();
    issueRecordCache = new Map();
    issueReferenceCache = new Map();
    commentsCache = new Map();
    blockingDependenciesCache = new Map();
    authenticatedUserLoginCache = {
        current: null,
    };
    repositoryOwnerKindCache = {
        current: null,
    };
    organizationMembershipCache = new Map();
    projectMembershipCache = new Map();
    knownCurrentLabelNamesByIssueId = new Map();
    constructor(baseClient) {
        this.baseClient = baseClient;
    }
    resetVolatileIssueReads(issueId) {
        this.issueCache.delete(issueId);
        this.issueRecordCache.delete(issueId);
        this.issueRecordSearchCache.clear();
        this.invalidateComments(issueId);
        this.blockingDependenciesCache.delete(issueId);
    }
    async listIssues(query) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.issueListCache, buildIssueListCacheKey(query), async () => {
            const issues = await this.baseClient.listIssues(query);
            for (const issue of issues) {
                this.rememberKnownIssueLabels(issue);
                if (canPrimeIssueCacheFromListResult(issue, query)) {
                    this.issueCache.set(issue.id, Promise.resolve(issue));
                }
            }
            return issues;
        });
    }
    async listIssueRecords(query) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.issueRecordListCache, buildIssueListCacheKey(query), async () => {
            const issues = await this.baseClient.listIssueRecords(query);
            for (const issue of issues) {
                this.rememberKnownIssueLabels(issue);
                this.issueRecordCache.set(issue.id, Promise.resolve(issue));
                this.issueReferenceCache.set(issue.id, Promise.resolve(toTrackerIssueReference(issue)));
            }
            return issues;
        });
    }
    async listServiceManagedDeltaIssues(query) {
        return this.baseClient.listServiceManagedDeltaIssues(query);
    }
    async listServiceConfigScanActivity(input) {
        if (typeof this.baseClient.listServiceConfigScanActivity !== "function") {
            return {
                candidateIssueIds: [],
                nextCursor: input.cursor,
                completed: true,
            };
        }
        return this.baseClient.listServiceConfigScanActivity(input);
    }
    async searchIssueRecords(query) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.issueRecordSearchCache, buildIssueRecordSearchCacheKey(query), async () => {
            const issues = await this.baseClient.searchIssueRecords(query);
            for (const issue of issues) {
                this.rememberKnownIssueLabels(issue);
                this.issueRecordCache.set(issue.id, Promise.resolve(issue));
                this.issueReferenceCache.set(issue.id, Promise.resolve(toTrackerIssueReference(issue)));
            }
            return issues;
        });
    }
    async getIssue(issueId) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.issueCache, issueId, async () => {
            const issue = await this.baseClient.getIssue(issueId);
            this.rememberKnownIssueLabels(issue);
            this.issueRecordCache.set(issue.id, Promise.resolve(issue));
            this.issueReferenceCache.set(issue.id, Promise.resolve(toTrackerIssueReference(issue)));
            return issue;
        });
    }
    async getIssueRecord(issueId) {
        const cachedIssue = this.issueCache.get(issueId);
        if (cachedIssue) {
            return cachedIssue;
        }
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.issueRecordCache, issueId, async () => {
            const issue = await this.baseClient.getIssueRecord(issueId);
            this.rememberKnownIssueLabels(issue);
            this.issueReferenceCache.set(issue.id, Promise.resolve(toTrackerIssueReference(issue)));
            return issue;
        });
    }
    async getPullRequestThreadRecord(input) {
        const issue = await this.baseClient.getPullRequestThreadRecord(input);
        if (issue === null) {
            return null;
        }
        this.rememberKnownIssueLabels(issue);
        this.issueRecordCache.set(issue.id, Promise.resolve(issue));
        this.issueReferenceCache.set(issue.id, Promise.resolve(toTrackerIssueReference(issue)));
        return issue;
    }
    async getIssueReference(issueId) {
        const cachedIssue = this.issueCache.get(issueId);
        if (cachedIssue) {
            return toTrackerIssueReference(await cachedIssue);
        }
        const cachedIssueRecord = this.issueRecordCache.get(issueId);
        if (cachedIssueRecord) {
            return toTrackerIssueReference(await cachedIssueRecord);
        }
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.issueReferenceCache, issueId, async () => this.baseClient.getIssueReference(issueId));
    }
    async getIssueCleanupState(issueId) {
        return this.baseClient.getIssueCleanupState(issueId);
    }
    async createIssue(input) {
        const issue = await this.baseClient.createIssue(input);
        this.invalidateIssue(issue.id);
        this.invalidateIssueLists();
        return issue;
    }
    async patchIssueRecord(issueId, input) {
        const issue = await this.baseClient.patchIssueRecord(issueId, input);
        this.invalidateIssue(issue.id);
        this.invalidateIssueLists();
        return issue;
    }
    async patchIssue(issueId, input) {
        const issue = await this.baseClient.patchIssue(issueId, input);
        this.invalidateIssue(issue.id);
        this.invalidateIssueLists();
        return issue;
    }
    async closeIssue(issueId) {
        await this.baseClient.closeIssue(issueId);
        this.invalidateIssue(issueId);
        this.invalidateIssueLists();
    }
    async listBlockingDependencies(issueId) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.blockingDependenciesCache, issueId, async () => this.baseClient.listBlockingDependencies(issueId));
    }
    async addBlockingDependency(issueId, blockedByIssueId) {
        await this.baseClient.addBlockingDependency(issueId, blockedByIssueId);
        this.invalidateIssue(issueId);
    }
    async removeBlockingDependency(issueId, blockedByIssueId) {
        await this.baseClient.removeBlockingDependency(issueId, blockedByIssueId);
        this.invalidateIssue(issueId);
    }
    async getAuthenticatedUserLogin() {
        return (0, promise_cache_1.getOrLoadPromiseCacheSlot)(this.authenticatedUserLoginCache, () => this.baseClient.getAuthenticatedUserLogin());
    }
    async getRepositoryOwnerKind() {
        return (0, promise_cache_1.getOrLoadPromiseCacheSlot)(this.repositoryOwnerKindCache, () => this.baseClient.getRepositoryOwnerKind());
    }
    async getOrganizationMembership(login) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.organizationMembershipCache, login, async () => this.baseClient.getOrganizationMembership(login));
    }
    async getProjectMembership(login) {
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.projectMembershipCache, login, async () => this.baseClient.getProjectMembership(login));
    }
    async listComments(issueId, options) {
        const timeline = options?.timeline ?? "full";
        return (0, promise_cache_1.getOrLoadPromiseCacheEntry)(this.commentsCache, buildCommentCacheKey(issueId, timeline), async () => this.baseClient.listComments(issueId, { timeline }));
    }
    async acknowledgeComment(comment) {
        await this.baseClient.acknowledgeComment(comment);
    }
    async createComment(issueId, body) {
        const comment = await this.baseClient.createComment(issueId, body);
        this.invalidateIssue(issueId);
        this.invalidateIssueLists();
        return comment;
    }
    async updateComment(commentId, body) {
        const comment = await this.baseClient.updateComment(commentId, body);
        const issueId = resolveIssueIdFromTrackerComment(comment);
        if (issueId !== null) {
            this.invalidateIssue(issueId);
            this.invalidateIssueLists();
        }
        else {
            this.issueCache.clear();
            this.commentsCache.clear();
            this.blockingDependenciesCache.clear();
            this.invalidateIssueLists();
            this.knownCurrentLabelNamesByIssueId.clear();
        }
        return comment;
    }
    async replaceAssignees(issueId, assignees) {
        await this.baseClient.replaceAssignees(issueId, assignees);
        this.invalidateIssue(issueId);
        this.invalidateIssueLists();
    }
    async updateIssueTitle(issueId, title) {
        await this.baseClient.updateIssueTitle(issueId, title);
        this.invalidateIssue(issueId);
        this.invalidateIssueLists();
    }
    async syncWorkflowState(input) {
        const currentLabelNames = input.currentLabelNames ??
            this.knownCurrentLabelNamesByIssueId.get(input.issueId);
        await this.baseClient.syncWorkflowState((0, issue_normalization_1.buildTrackerWorkflowStateSyncInput)(input, currentLabelNames));
        this.invalidateIssue(input.issueId);
        this.invalidateIssueLists();
    }
    async ensureWorkflowSchema(options) {
        const result = await this.baseClient.ensureWorkflowSchema(options);
        this.invalidateIssueLists();
        this.knownCurrentLabelNamesByIssueId.clear();
        return result;
    }
    async checkWorkflowSchema() {
        return this.baseClient.checkWorkflowSchema();
    }
    invalidateIssue(issueId) {
        this.issueCache.delete(issueId);
        this.issueRecordCache.delete(issueId);
        this.issueReferenceCache.delete(issueId);
        this.invalidateComments(issueId);
        this.blockingDependenciesCache.delete(issueId);
        this.knownCurrentLabelNamesByIssueId.delete(issueId);
    }
    invalidateIssueLists() {
        this.issueListCache.clear();
        this.issueRecordListCache.clear();
        this.issueRecordSearchCache.clear();
    }
    invalidateComments(issueId) {
        this.commentsCache.delete(buildCommentCacheKey(issueId, "full"));
        this.commentsCache.delete(buildCommentCacheKey(issueId, "issue-only"));
    }
    rememberKnownIssueLabels(issue) {
        if (Array.isArray(issue.labelNames)) {
            this.knownCurrentLabelNamesByIssueId.set(issue.id, [...issue.labelNames]);
        }
    }
}
function buildIssueRecordSearchCacheKey(query) {
    return JSON.stringify({
        state: query.state ?? null,
        mode: query.mode,
        text: query.text.trim().toLowerCase(),
        limit: normalizeIssueRecordSearchCacheLimit(query.limit),
        includePullRequests: query.includePullRequests ?? false,
    });
}
function normalizeIssueRecordSearchCacheLimit(limit) {
    if (limit === undefined) {
        return "default";
    }
    if (limit === null) {
        return "all";
    }
    return Math.max(0, Math.floor(limit));
}
function toTrackerIssueReference(issue) {
    return {
        id: issue.id,
        displayId: issue.displayId,
        title: issue.title,
        baseTitle: issue.baseTitle,
        isOpen: issue.isOpen,
    };
}
function buildIssueListCacheKey(query) {
    const normalizedQuery = (0, issue_normalization_1.normalizeTrackerIssueListQuery)(query);
    const normalizedLabelNames = [...(normalizedQuery.labelNames ?? [])].sort();
    return JSON.stringify({
        state: normalizedQuery.state ?? null,
        since: normalizedQuery.since ?? null,
        labelNames: normalizedLabelNames,
        workflowStatus: normalizedQuery.workflowStatus ?? null,
        includeSourceBranchName: normalizedQuery.includeSourceBranchName,
    });
}
function canPrimeIssueCacheFromListResult(issue, query) {
    const normalizedQuery = (0, issue_normalization_1.normalizeTrackerIssueListQuery)(query);
    if (issue.kind !== "pull_request") {
        return true;
    }
    if (!normalizedQuery.includeSourceBranchName) {
        return typeof issue.sourceBranchName === "string";
    }
    return true;
}
function buildCommentCacheKey(issueId, timeline = "full") {
    return JSON.stringify({
        issueId,
        timeline,
    });
}
function resolveIssueIdFromTrackerComment(comment) {
    const targetDisplayId = comment.targetDisplayId.trim();
    if (!targetDisplayId.startsWith("#")) {
        return null;
    }
    const issueId = targetDisplayId.slice(1).trim();
    return /^\d+$/u.test(issueId) ? issueId : null;
}
