// =============================================================================
// Pure Gitea API — no ToolContext coupling, no registry coupling.
//
// Takes a plain { apiUrl, apiToken, username } config. Security guardrails
// (PROTECTED_GITEA_REPOS, assertGiteaWritable) are enforced inside each
// function so every caller gets the same protection.
//
// Consumed by:
//   - tools/gitea.ts  (in-process registry used by agent-runner loop)
//   - mcp/gitea-server.ts  (stdio MCP server exposed to any MCP client)
// =============================================================================

import { PROTECTED_GITEA_REPOS, assertGiteaWritable } from './security';

export interface GiteaConfig {
    apiUrl: string;
    apiToken: string;
    username?: string;
}

type GiteaError = { error: string };

async function giteaFetch(
    cfg: GiteaConfig,
    path: string,
    method: string = 'GET',
    body?: unknown
): Promise<unknown> {
    const res = await fetch(`${cfg.apiUrl}/api/v1${path}`, {
        method,
        headers: {
            'Authorization': `token ${cfg.apiToken}`,
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        body: body ? JSON.stringify(body) : undefined
    });
    if (!res.ok) {
        return { error: `Gitea API error: ${res.status} ${res.statusText}` } satisfies GiteaError;
    }
    return res.json();
}

// ---------------------------------------------------------------------------
// Public API — 1:1 with tool surface
// ---------------------------------------------------------------------------

export interface CreateIssueInput {
    repo: string;
    title: string;
    body: string;
    labels?: string[];
}

export async function createIssue(cfg: GiteaConfig, input: CreateIssueInput): Promise<unknown> {
    assertGiteaWritable(input.repo);
    return giteaFetch(cfg, `/repos/${input.repo}/issues`, 'POST', {
        title: input.title,
        body: input.body,
        labels: input.labels,
    });
}

export async function listIssues(cfg: GiteaConfig, repo: string, state: string = 'open'): Promise<unknown> {
    return giteaFetch(cfg, `/repos/${repo}/issues?state=${state}&limit=20`);
}

export async function closeIssue(cfg: GiteaConfig, repo: string, issueNumber: number): Promise<unknown> {
    assertGiteaWritable(repo);
    return giteaFetch(cfg, `/repos/${repo}/issues/${issueNumber}`, 'PATCH', { state: 'closed' });
}

export async function listRepos(cfg: GiteaConfig): Promise<unknown> {
    const res = await fetch(`${cfg.apiUrl}/api/v1/repos/search?limit=50`, {
        headers: { 'Authorization': `token ${cfg.apiToken}` }
    });
    if (!res.ok) {
        return { error: `Gitea API error: ${res.status} ${res.statusText}` } satisfies GiteaError;
    }
    const data = await res.json() as any;
    return (data.data || [])
        .filter((r: any) => !PROTECTED_GITEA_REPOS.has(r.full_name))
        .map((r: any) => ({
            name: r.full_name,
            description: r.description,
            default_branch: r.default_branch,
            updated: r.updated,
            stars: r.stars_count,
            open_issues: r.open_issues_count,
        }));
}

export async function listAllIssues(
    cfg: GiteaConfig,
    opts: { repo?: string; state?: string } = {}
): Promise<unknown> {
    const state = opts.state || 'open';

    if (opts.repo) {
        if (PROTECTED_GITEA_REPOS.has(opts.repo)) {
            return {
                error: `SECURITY: "${opts.repo}" is a protected Vibn platform repo. Agents cannot access its issues.`
            } satisfies GiteaError;
        }
        return giteaFetch(cfg, `/repos/${opts.repo}/issues?state=${state}&limit=20`);
    }

    // Fetch across all non-protected repos (cap at 10 repos to bound request count)
    const reposRes = await fetch(`${cfg.apiUrl}/api/v1/repos/search?limit=50`, {
        headers: { 'Authorization': `token ${cfg.apiToken}` }
    });
    if (!reposRes.ok) {
        return { error: `Gitea API error: ${reposRes.status} ${reposRes.statusText}` } satisfies GiteaError;
    }
    const reposData = await reposRes.json() as any;
    const repos = (reposData.data || []).filter((r: any) => !PROTECTED_GITEA_REPOS.has(r.full_name));

    const allIssues: unknown[] = [];
    for (const r of repos.slice(0, 10)) {
        const issues = await giteaFetch(cfg, `/repos/${r.full_name}/issues?state=${state}&limit=10`) as any[];
        if (Array.isArray(issues)) {
            allIssues.push(...issues.map((i: any) => ({
                repo: r.full_name,
                number: i.number,
                title: i.title,
                state: i.state,
                labels: i.labels?.map((l: any) => l.name),
                created: i.created_at,
            })));
        }
    }
    return allIssues;
}

export async function readRepoFile(cfg: GiteaConfig, repo: string, filePath: string): Promise<unknown> {
    try {
        const res = await fetch(`${cfg.apiUrl}/api/v1/repos/${repo}/contents/${filePath}`, {
            headers: { 'Authorization': `token ${cfg.apiToken}` }
        });
        if (!res.ok) return { error: `File not found: ${filePath} in ${repo}` } satisfies GiteaError;
        const data = await res.json() as any;
        const content = Buffer.from(data.content, 'base64').toString('utf8');
        return { repo, path: filePath, content };
    } catch (err) {
        return {
            error: `Failed to read ${filePath}: ${err instanceof Error ? err.message : String(err)}`
        } satisfies GiteaError;
    }
}
