import { ExternalIssueAdapterError, toNormalizedEnvelope, } from './types.js';
function ensureAsanaConfig(config) {
    const accessToken = config.accessToken || process.env.PRLT_ASANA_ACCESS_TOKEN || process.env.ASANA_ACCESS_TOKEN;
    if (!accessToken) {
        throw new ExternalIssueAdapterError('MISSING_CONFIG', 'Missing Asana access token. Set ASANA_ACCESS_TOKEN or PRLT_ASANA_ACCESS_TOKEN, or run "prlt asana connect".');
    }
    return {
        accessToken,
        projectGid: config.projectGid || process.env.PRLT_ASANA_PROJECT_GID,
        workspaceGid: config.workspaceGid || process.env.PRLT_ASANA_WORKSPACE_GID,
    };
}
function deriveAsanaUrl(task) {
    if (task.permalink_url) {
        return task.permalink_url;
    }
    return `https://app.asana.com/0/0/${task.gid}`;
}
function deriveAsanaStatus(task) {
    if (task.completed) {
        return 'Completed';
    }
    const section = task.memberships?.[0]?.section?.name;
    if (section) {
        return section;
    }
    return 'Open';
}
function deriveAsanaProjectKey(task) {
    const project = task.memberships?.[0]?.project;
    return project?.name || project?.gid || 'ASANA';
}
function deriveAsanaLabels(task) {
    if (!Array.isArray(task.tags)) {
        return [];
    }
    return task.tags
        .map(tag => tag.name?.trim())
        .filter((name) => Boolean(name));
}
/**
 * Normalize a raw Asana task into a canonical IssueEnvelope.
 */
export function normalizeAsanaTask(rawTask) {
    if (!rawTask || typeof rawTask !== 'object') {
        throw new ExternalIssueAdapterError('BAD_PAYLOAD', 'Asana task payload is invalid.', rawTask);
    }
    const task = rawTask;
    if (!task.gid || !task.name) {
        throw new ExternalIssueAdapterError('BAD_PAYLOAD', 'Asana task payload is missing required fields (gid, name).', task);
    }
    return {
        source: 'asana',
        external_id: task.gid,
        external_key: task.gid,
        title: task.name,
        description: task.notes || '',
        labels: deriveAsanaLabels(task),
        priority: null,
        status: deriveAsanaStatus(task),
        url: deriveAsanaUrl(task),
        project_key: deriveAsanaProjectKey(task),
        assignee: task.assignee?.name || null,
        item_type: 'task',
        raw: rawTask,
    };
}
/**
 * Normalize a raw Asana task into a PMO-ready NormalizedIssueEnvelope.
 */
export function normalizeAsanaTaskToEnvelope(rawTask) {
    const envelope = normalizeAsanaTask(rawTask);
    return toNormalizedEnvelope(envelope, 'feature');
}
/**
 * Build a PMO ticket description from a NormalizedIssueEnvelope.
 */
export function buildAsanaTicketDescription(envelope) {
    const body = envelope.description.trim();
    const metadataLines = [
        `- Source: ${envelope.source.name}`,
        `- External key: ${envelope.source.externalKey}`,
        `- External id: ${envelope.source.externalId}`,
        `- URL: ${envelope.source.url}`,
        `- Status: ${envelope.status}`,
        `- Priority: ${envelope.priority || 'Unset'}`,
        `- Labels: ${envelope.labels.length > 0 ? envelope.labels.join(', ') : 'None'}`,
    ];
    const parts = [
        body,
        '## External Issue Context',
        metadataLines.join('\n'),
    ].filter(Boolean);
    return parts.join('\n\n');
}
/**
 * Build ticket metadata from a NormalizedIssueEnvelope for traceability.
 */
export function buildAsanaMetadata(envelope) {
    return {
        external_source: envelope.source.name,
        external_key: envelope.source.externalKey,
        external_id: envelope.source.externalId,
        external_url: envelope.source.url,
        external_raw: JSON.stringify(envelope.source.raw),
    };
}
/**
 * Build a spawn context message from a NormalizedIssueEnvelope.
 */
export function buildAsanaSpawnContextMessage(envelope, additionalMessage) {
    const lines = [
        `External issue source: ${envelope.source.name}`,
        `External issue key: ${envelope.source.externalKey}`,
        `External issue id: ${envelope.source.externalId}`,
        `External issue URL: ${envelope.source.url}`,
    ];
    if (additionalMessage?.trim()) {
        lines.push('', additionalMessage.trim());
    }
    return lines.join('\n');
}
/**
 * Build a CLI command string for selecting a specific Asana task.
 */
export function buildAsanaTaskChoiceCommand(taskGid, projectId) {
    let command = `prlt work asana --task ${taskGid} --json`;
    if (projectId) {
        command += ` -P ${projectId}`;
    }
    return command;
}
/**
 * Fetch a single Asana task by GID and normalize it.
 */
export async function getAsanaTaskByGid(configInput, taskGid, options) {
    const config = ensureAsanaConfig(configInput);
    const fetchImpl = options?.fetchImpl || fetch;
    const optFields = 'gid,name,completed,notes,assignee.gid,assignee.name,tags.gid,tags.name,memberships.project.gid,memberships.project.name,memberships.section.gid,memberships.section.name,due_on,permalink_url';
    const url = `https://app.asana.com/api/1.0/tasks/${encodeURIComponent(taskGid)}?opt_fields=${encodeURIComponent(optFields)}`;
    // eslint-disable-next-line n/no-unsupported-features/node-builtins -- fetch is available in supported Node runtimes for this CLI
    const response = await fetchImpl(url, {
        method: 'GET',
        headers: {
            Authorization: `Bearer ${config.accessToken}`,
            'Content-Type': 'application/json',
        },
    });
    if (response.status === 401 || response.status === 403) {
        throw new ExternalIssueAdapterError('AUTH_FAILED', 'Asana authentication failed. Verify your Asana access token.');
    }
    if (response.status === 404) {
        return null;
    }
    if (!response.ok) {
        throw new ExternalIssueAdapterError('REQUEST_FAILED', `Asana request failed with status ${response.status}.`);
    }
    const payload = await response.json();
    if (payload.errors && payload.errors.length > 0) {
        const message = payload.errors[0]?.message || 'Unknown Asana API error.';
        throw new ExternalIssueAdapterError('REQUEST_FAILED', `Asana API error: ${message}`);
    }
    const task = payload.data;
    if (!task)
        return null;
    return normalizeAsanaTaskToEnvelope(task);
}
/**
 * Fetch and normalize Asana tasks from a project into NormalizedIssueEnvelopes.
 */
export async function listAsanaTasks(configInput, options) {
    const config = ensureAsanaConfig(configInput);
    if (!config.projectGid) {
        throw new ExternalIssueAdapterError('MISSING_CONFIG', 'Missing Asana project GID. Run "prlt asana connect" and select a project, or set PRLT_ASANA_PROJECT_GID.');
    }
    const fetchImpl = options?.fetchImpl || fetch;
    const limit = Math.max(1, Math.min(options?.limit ?? 50, 100));
    const optFields = 'gid,name,completed,notes,assignee.gid,assignee.name,tags.gid,tags.name,memberships.project.gid,memberships.project.name,memberships.section.gid,memberships.section.name,due_on,permalink_url';
    const url = `https://app.asana.com/api/1.0/projects/${encodeURIComponent(config.projectGid)}/tasks?opt_fields=${encodeURIComponent(optFields)}&limit=${limit}&completed_since=now`;
    // eslint-disable-next-line n/no-unsupported-features/node-builtins -- fetch is available in supported Node runtimes for this CLI
    const response = await fetchImpl(url, {
        method: 'GET',
        headers: {
            Authorization: `Bearer ${config.accessToken}`,
            'Content-Type': 'application/json',
        },
    });
    if (response.status === 401 || response.status === 403) {
        throw new ExternalIssueAdapterError('AUTH_FAILED', 'Asana authentication failed. Verify your Asana access token.');
    }
    if (!response.ok) {
        throw new ExternalIssueAdapterError('REQUEST_FAILED', `Asana request failed with status ${response.status}.`);
    }
    const payload = await response.json();
    if (payload.errors && payload.errors.length > 0) {
        const message = payload.errors[0]?.message || 'Unknown Asana API error.';
        throw new ExternalIssueAdapterError('REQUEST_FAILED', `Asana API error: ${message}`);
    }
    const tasks = payload.data;
    if (!Array.isArray(tasks)) {
        throw new ExternalIssueAdapterError('BAD_PAYLOAD', 'Asana response payload was missing tasks array.', payload);
    }
    return tasks.map(normalizeAsanaTaskToEnvelope);
}
/**
 * Import an Asana task into PMO as a linked ticket.
 */
export async function importAsanaTaskToPmo(storage, projectId, envelope) {
    const tickets = await storage.listTickets(projectId);
    const existing = tickets.find((ticket) => {
        const source = ticket.metadata?.external_source;
        const key = ticket.metadata?.external_key;
        const id = ticket.metadata?.external_id;
        return source === 'asana'
            && (key === envelope.source.externalKey || id === envelope.source.externalId);
    });
    const description = buildAsanaTicketDescription(envelope);
    const metadata = buildAsanaMetadata(envelope);
    if (existing) {
        await storage.updateTicket(existing.id, {
            title: envelope.title,
            description,
            priority: envelope.priority ?? undefined,
            category: envelope.category ?? undefined,
            labels: envelope.labels,
            metadata: {
                ...existing.metadata,
                ...metadata,
            },
        });
        return { ticketId: existing.id, created: false };
    }
    const ticket = await storage.createTicket(projectId, {
        title: envelope.title,
        description,
        priority: envelope.priority ?? undefined,
        category: envelope.category ?? undefined,
        labels: envelope.labels,
        metadata,
    });
    return { ticketId: ticket.id, created: true };
}
//# sourceMappingURL=asana.js.map