import { and, desc, eq, inArray } from "drizzle-orm";
import { approvals, issueApprovals, issues } from "@zmeel/db";
import { notFound, unprocessable } from "../errors.js";
import { redactEventPayload } from "../redaction.js";
export function issueApprovalService(db) {
    async function getIssue(issueId) {
        return db
            .select()
            .from(issues)
            .where(eq(issues.id, issueId))
            .then((rows) => rows[0] ?? null);
    }
    async function getApproval(approvalId) {
        return db
            .select()
            .from(approvals)
            .where(eq(approvals.id, approvalId))
            .then((rows) => rows[0] ?? null);
    }
    async function assertIssueAndApprovalSameCompany(issueId, approvalId) {
        const issue = await getIssue(issueId);
        if (!issue)
            throw notFound("Issue not found");
        const approval = await getApproval(approvalId);
        if (!approval)
            throw notFound("Approval not found");
        if (issue.companyId !== approval.companyId) {
            throw unprocessable("Issue and approval must belong to the same company");
        }
        return { issue, approval };
    }
    return {
        listApprovalsForIssue: async (issueId) => {
            const issue = await getIssue(issueId);
            if (!issue)
                throw notFound("Issue not found");
            const result = await db
                .select({
                id: approvals.id,
                companyId: approvals.companyId,
                type: approvals.type,
                requestedByAgentId: approvals.requestedByAgentId,
                requestedByUserId: approvals.requestedByUserId,
                status: approvals.status,
                payload: approvals.payload,
                decisionNote: approvals.decisionNote,
                decidedByUserId: approvals.decidedByUserId,
                decidedAt: approvals.decidedAt,
                createdAt: approvals.createdAt,
                updatedAt: approvals.updatedAt,
            })
                .from(issueApprovals)
                .innerJoin(approvals, eq(issueApprovals.approvalId, approvals.id))
                .where(eq(issueApprovals.issueId, issueId))
                .orderBy(desc(issueApprovals.createdAt));
            return result.map((approval) => ({
                ...approval,
                payload: redactEventPayload(approval.payload) ?? {},
            }));
        },
        listIssuesForApproval: async (approvalId) => {
            const approval = await getApproval(approvalId);
            if (!approval)
                throw notFound("Approval not found");
            return db
                .select({
                id: issues.id,
                companyId: issues.companyId,
                projectId: issues.projectId,
                goalId: issues.goalId,
                parentId: issues.parentId,
                title: issues.title,
                description: issues.description,
                status: issues.status,
                priority: issues.priority,
                assigneeAgentId: issues.assigneeAgentId,
                createdByAgentId: issues.createdByAgentId,
                createdByUserId: issues.createdByUserId,
                issueNumber: issues.issueNumber,
                identifier: issues.identifier,
                requestDepth: issues.requestDepth,
                billingCode: issues.billingCode,
                startedAt: issues.startedAt,
                completedAt: issues.completedAt,
                cancelledAt: issues.cancelledAt,
                createdAt: issues.createdAt,
                updatedAt: issues.updatedAt,
            })
                .from(issueApprovals)
                .innerJoin(issues, eq(issueApprovals.issueId, issues.id))
                .where(eq(issueApprovals.approvalId, approvalId))
                .orderBy(desc(issueApprovals.createdAt));
        },
        link: async (issueId, approvalId, actor) => {
            const { issue } = await assertIssueAndApprovalSameCompany(issueId, approvalId);
            await db
                .insert(issueApprovals)
                .values({
                companyId: issue.companyId,
                issueId,
                approvalId,
                linkedByAgentId: actor?.agentId ?? null,
                linkedByUserId: actor?.userId ?? null,
            })
                .onConflictDoNothing();
            return db
                .select()
                .from(issueApprovals)
                .where(and(eq(issueApprovals.issueId, issueId), eq(issueApprovals.approvalId, approvalId)))
                .then((rows) => rows[0] ?? null);
        },
        unlink: async (issueId, approvalId) => {
            await assertIssueAndApprovalSameCompany(issueId, approvalId);
            await db
                .delete(issueApprovals)
                .where(and(eq(issueApprovals.issueId, issueId), eq(issueApprovals.approvalId, approvalId)));
        },
        linkManyForApproval: async (approvalId, issueIds, actor) => {
            if (issueIds.length === 0)
                return;
            const approval = await getApproval(approvalId);
            if (!approval)
                throw notFound("Approval not found");
            const uniqueIssueIds = Array.from(new Set(issueIds));
            const rows = await db
                .select({
                id: issues.id,
                companyId: issues.companyId,
            })
                .from(issues)
                .where(inArray(issues.id, uniqueIssueIds));
            if (rows.length !== uniqueIssueIds.length) {
                throw notFound("One or more issues not found");
            }
            for (const row of rows) {
                if (row.companyId !== approval.companyId) {
                    throw unprocessable("Issue and approval must belong to the same company");
                }
            }
            await db
                .insert(issueApprovals)
                .values(uniqueIssueIds.map((issueId) => ({
                companyId: approval.companyId,
                issueId,
                approvalId,
                linkedByAgentId: actor?.agentId ?? null,
                linkedByUserId: actor?.userId ?? null,
            })))
                .onConflictDoNothing();
        },
    };
}
//# sourceMappingURL=issue-approvals.js.map