import express, { Request, Response, NextFunction } from 'express';
import cors from 'cors';
import { createJob, getJob, listJobs, updateJob } from './job-store';
import { runAgent } from './agent-runner';
import { AGENTS } from './agents';
import { ToolContext } from './tools';

const app = express();
app.use(cors());
app.use(express.json());

const PORT = process.env.PORT || 3333;

// ---------------------------------------------------------------------------
// Build ToolContext from environment variables
// ---------------------------------------------------------------------------

function buildContext(repo?: string): ToolContext {
    const workspaceRoot = repo
        ? `${process.env.WORKSPACE_BASE || '/workspaces'}/${repo.replace('/', '_')}`
        : (process.env.WORKSPACE_BASE || '/workspaces/default');

    return {
        workspaceRoot,
        gitea: {
            apiUrl: process.env.GITEA_API_URL || '',
            apiToken: process.env.GITEA_API_TOKEN || '',
            username: process.env.GITEA_USERNAME || ''
        },
        coolify: {
            apiUrl: process.env.COOLIFY_API_URL || '',
            apiToken: process.env.COOLIFY_API_TOKEN || ''
        }
    };
}

// ---------------------------------------------------------------------------
// Routes
// ---------------------------------------------------------------------------

// Health check
app.get('/health', (_req: Request, res: Response) => {
    res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// List available agents
app.get('/api/agents', (_req: Request, res: Response) => {
    const agents = Object.values(AGENTS).map(a => ({
        name: a.name,
        description: a.description,
        tools: a.tools.map(t => t.name)
    }));
    res.json(agents);
});

// Submit a new job
app.post('/api/agent/run', async (req: Request, res: Response) => {
    const { agent: agentName, task, repo } = req.body as { agent?: string; task?: string; repo?: string };

    if (!agentName || !task) {
        res.status(400).json({ error: '"agent" and "task" are required' });
        return;
    }

    const agentConfig = AGENTS[agentName];
    if (!agentConfig) {
        const available = Object.keys(AGENTS).join(', ');
        res.status(400).json({ error: `Unknown agent "${agentName}". Available: ${available}` });
        return;
    }

    const job = createJob(agentName, task, repo);
    res.status(202).json({ jobId: job.id, status: job.status });

    // Run agent asynchronously
    const ctx = buildContext(repo);
    runAgent(job, agentConfig, task, ctx)
        .then(result => {
            updateJob(job.id, {
                status: 'completed',
                result: result.finalText,
                progress: `Done — ${result.turns} turns, ${result.toolCallCount} tool calls`
            });
        })
        .catch(err => {
            updateJob(job.id, {
                status: 'failed',
                error: err instanceof Error ? err.message : String(err),
                progress: 'Agent failed'
            });
        });
});

// Check job status
app.get('/api/jobs/:id', (req: Request, res: Response) => {
    const job = getJob(req.params.id);
    if (!job) {
        res.status(404).json({ error: 'Job not found' });
        return;
    }
    res.json(job);
});

// List recent jobs
app.get('/api/jobs', (req: Request, res: Response) => {
    const limit = parseInt((req.query.limit as string) || '20', 10);
    res.json(listJobs(limit));
});

// Gitea webhook endpoint — triggers agent from a push/issue event
app.post('/webhook/gitea', (req: Request, res: Response) => {
    const event = req.headers['x-gitea-event'] as string;
    const body = req.body as any;

    // Verify secret if configured
    const webhookSecret = process.env.WEBHOOK_SECRET;
    if (webhookSecret) {
        const sig = req.headers['x-gitea-signature'] as string;
        if (!sig || sig !== webhookSecret) {
            res.status(401).json({ error: 'Invalid webhook signature' });
            return;
        }
    }

    let task: string | null = null;
    let agentName = 'Coder';
    let repo: string | undefined;

    if (event === 'issues' && body.action === 'opened') {
        const issue = body.issue;
        repo = `${body.repository?.owner?.login}/${body.repository?.name}`;
        const labels: string[] = (issue.labels || []).map((l: any) => l.name as string);

        if (labels.includes('agent:pm')) {
            agentName = 'PM';
        } else if (labels.includes('agent:marketing')) {
            agentName = 'Marketing';
        } else {
            agentName = 'Coder';
        }

        task = `Resolve this GitHub issue:\n\nTitle: ${issue.title}\n\nDescription:\n${issue.body || '(no description)'}`;
    } else if (event === 'push') {
        // Optionally trigger on push — useful for CI-style automation
        res.json({ ignored: true, reason: 'push events not auto-processed' });
        return;
    } else {
        res.json({ ignored: true, event });
        return;
    }

    if (!task) {
        res.json({ ignored: true });
        return;
    }

    const agentConfig = AGENTS[agentName];
    const job = createJob(agentName, task, repo);
    res.status(202).json({ jobId: job.id, agent: agentName, event });

    const ctx = buildContext(repo);
    runAgent(job, agentConfig, task, ctx)
        .then(result => {
            updateJob(job.id, {
                status: 'completed',
                result: result.finalText,
                progress: `Done — ${result.turns} turns, ${result.toolCallCount} tool calls`
            });
        })
        .catch(err => {
            updateJob(job.id, {
                status: 'failed',
                error: err instanceof Error ? err.message : String(err),
                progress: 'Agent failed'
            });
        });
});

// ---------------------------------------------------------------------------
// Error handler
// ---------------------------------------------------------------------------

app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
    console.error(err.stack);
    res.status(500).json({ error: err.message });
});

// ---------------------------------------------------------------------------
// Start
// ---------------------------------------------------------------------------

app.listen(PORT, () => {
    console.log(`AgentRunner listening on port ${PORT}`);
    console.log(`Agents available: ${Object.keys(AGENTS).join(', ')}`);
    if (!process.env.GOOGLE_API_KEY) {
        console.warn('WARNING: GOOGLE_API_KEY is not set — agents will fail');
    }
});
