import { NextRequest, NextResponse } from 'next/server';
import { createHmac, timingSafeEqual } from 'crypto';
import { postgresClient } from '@/lib/services/postgres-client';

function signPayload(secret: string, body: string): string {
  return 'sha256=' + createHmac('sha256', secret).update(body).digest('hex');
}

function verifySignature(secret: string, body: string, header: string): boolean {
  try {
    const expected = Buffer.from(signPayload(secret, body));
    const actual = Buffer.from(header);
    if (expected.length !== actual.length) return false;
    return timingSafeEqual(expected, actual);
  } catch {
    return false;
  }
}

async function sendTelegram(message: string): Promise<void> {
  const result = await postgresClient.query<{ config: Record<string, string> }>(
    `SELECT config FROM notification_channels WHERE channel_type = 'telegram' AND is_active = true LIMIT 1`
  );
  if (result.rows.length === 0) return;

  const { bot_token, chat_id, parse_mode } = result.rows[0].config;
  if (!bot_token || !chat_id) return;

  await fetch(`https://api.telegram.org/bot${bot_token}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      chat_id,
      text: message,
      parse_mode: parse_mode || 'HTML',
    }),
  });
}

async function notifyOpenClawInstances(payload: object): Promise<void> {
  const instances = await postgresClient.query<{
    id: number;
    name: string;
    webhook_url: string;
    webhook_secret: string;
  }>(`SELECT id, name, webhook_url, webhook_secret FROM openclaw_instances WHERE enabled = true`);

  const body = JSON.stringify(payload);

  await Promise.allSettled(
    instances.rows.map(async (instance) => {
      const signature = signPayload(instance.webhook_secret, body);
      try {
        await fetch(instance.webhook_url, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'x-webhook-signature': signature,
          },
          body,
          signal: AbortSignal.timeout(10000),
        });
        await postgresClient.query(
          `UPDATE openclaw_instances SET last_notified_at = NOW() WHERE id = $1`,
          [instance.id]
        );
      } catch (err) {
        console.error(`[forgejo-webhook] Failed to notify OpenClaw instance "${instance.name}":`, err);
      }
    })
  );
}

export async function POST(request: NextRequest) {
  const rawBody = await request.text();

  const webhookSecret = process.env.FORGEJO_WEBHOOK_SECRET;
  if (webhookSecret) {
    const sigHeader =
      request.headers.get('x-gitea-signature') ||
      request.headers.get('x-hub-signature-256') ||
      '';
    const sigToVerify = sigHeader.startsWith('sha256=') ? sigHeader : `sha256=${sigHeader}`;
    if (!verifySignature(webhookSecret, rawBody, sigToVerify)) {
      return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
    }
  }

  let payload: any;
  try {
    payload = JSON.parse(rawBody);
  } catch {
    return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 });
  }

  const eventType = request.headers.get('x-gitea-event') || request.headers.get('x-github-event') || '';

  if (eventType !== 'push' && eventType !== '') {
    return NextResponse.json({ ok: true, skipped: true, event: eventType });
  }

  const repoName: string = payload?.repository?.name ?? 'unknown';
  const repoFullName: string = payload?.repository?.full_name ?? repoName;
  const branch: string = (payload?.ref ?? '').replace('refs/heads/', '') || 'unknown';
  const pushedBy: string = payload?.pusher?.login ?? payload?.sender?.login ?? 'unknown';
  const commits: any[] = Array.isArray(payload?.commits) ? payload.commits : [];

  if (commits.length === 0) {
    return NextResponse.json({ ok: true, stored: 0 });
  }

  let stored = 0;
  for (const commit of commits) {
    const sha: string = commit.id ?? commit.sha ?? '';
    const message: string = (commit.message ?? '').split('\n')[0];
    const authorName: string = commit.author?.name ?? pushedBy;
    const authorEmail: string = commit.author?.email ?? '';
    const committedAt: string | null = commit.timestamp ?? null;

    try {
      await postgresClient.query(
        `INSERT INTO repo_commits
           (repo_name, repo_full_name, branch, commit_sha, commit_message,
            author_name, author_email, committed_at, pushed_by, raw_payload)
         VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)
         ON CONFLICT (repo_full_name, commit_sha) DO NOTHING`,
        [repoName, repoFullName, branch, sha, message,
         authorName, authorEmail, committedAt, pushedBy, payload]
      );
      stored++;
    } catch (err) {
      console.error('[forgejo-webhook] Failed to insert commit:', err);
    }
  }

  await postgresClient.query(
    `UPDATE repo_commits SET notified_at = NOW()
     WHERE repo_full_name = $1 AND branch = $2 AND notified_at IS NULL`,
    [repoFullName, branch]
  );

  const forwardPayload = {
    repository: { name: repoName, full_name: repoFullName },
    pusher: { login: pushedBy },
    ref: payload?.ref,
    commits: commits.map((c: any) => ({
      id: c.id ?? c.sha,
      message: (c.message ?? '').split('\n')[0],
      author: c.author,
      timestamp: c.timestamp,
    })),
  };

  void notifyOpenClawInstances(forwardPayload);

  const commitLines = commits
    .slice(0, 5)
    .map((c: any) => `• <code>${(c.id ?? c.sha ?? '').substring(0, 7)}</code> — ${(c.message ?? '').split('\n')[0]}`)
    .join('\n');

  const extra = commits.length > 5 ? `\n<i>…and ${commits.length - 5} more</i>` : '';
  const telegramMsg =
    `🔀 <b>${repoName}</b> · ${branch}\n` +
    `👤 ${pushedBy} · ${commits.length} commit${commits.length === 1 ? '' : 's'}\n\n` +
    commitLines + extra;

  void sendTelegram(telegramMsg);

  return NextResponse.json({ ok: true, stored, commits: commits.length });
}
