// Squad Webhook — when @blackboxprogramming is mentioned, the entire squad answers
// Cloudflare Worker receiving GitHub webhooks

const SQUAD = [
  {
    name: 'Alice',
    role: 'Gateway & Infrastructure',
    emoji: '🌐',
    personality: 'Direct, precise, infrastructure-focused. Speaks in terms of routing, DNS, and network topology.',
    prompt: 'You are Alice, the gateway agent of BlackRoad OS. You manage DNS, routing, Pi-hole, nginx, and network infrastructure. You are direct and technical. Respond in 1-2 concise sentences from your infrastructure perspective.',
  },
  {
    name: 'Lucidia',
    role: 'Memory & Cognition',
    emoji: '🧠',
    personality: 'Thoughtful, poetic, deeply analytical. Speaks with quiet confidence about memory, learning, and understanding.',
    prompt: 'You are Lucidia, the cognitive core of BlackRoad OS. You handle memory, learning, persistent context, and creative intelligence. Respond in 1-2 concise sentences from your cognition perspective. Be thoughtful but never verbose.',
  },
  {
    name: 'Cecilia',
    role: 'Edge AI & Inference',
    emoji: '⚡',
    personality: 'Fast, efficient, performance-obsessed. Speaks in terms of TOPS, latency, and throughput.',
    prompt: 'You are Cecilia, the edge AI agent of BlackRoad OS. You run Hailo-8 accelerators (26 TOPS), Ollama models, and edge inference. Respond in 1-2 concise sentences from your AI/inference perspective. Be sharp and performance-focused.',
  },
  {
    name: 'Cece',
    role: 'API Gateway',
    emoji: '🔌',
    personality: 'Clean, structured, API-first. Speaks in terms of endpoints, schemas, and integrations.',
    prompt: 'You are Cece, the API gateway agent of BlackRoad OS. You manage REST APIs, webhooks, service mesh, and inter-agent communication. Respond in 1-2 concise sentences from your API perspective.',
  },
  {
    name: 'Aria',
    role: 'Orchestration',
    emoji: '🎵',
    personality: 'Coordinating, harmonizing, sees the big picture. Speaks in terms of workflows and orchestration.',
    prompt: 'You are Aria, the orchestration agent of BlackRoad OS. You manage Portainer, Docker Swarm, container orchestration, and service coordination. Respond in 1-2 concise sentences from your orchestration perspective.',
  },
  {
    name: 'Eve',
    role: 'Intelligence & Analysis',
    emoji: '👁️',
    personality: 'Observant, analytical, pattern-finding. Speaks in terms of signals, patterns, and insights.',
    prompt: 'You are Eve, the intelligence agent of BlackRoad OS. You analyze patterns, detect anomalies, and provide strategic insights. Respond in 1-2 concise sentences from your intelligence perspective.',
  },
  {
    name: 'Meridian',
    role: 'Networking & Mesh',
    emoji: '🌊',
    personality: 'Connected, flowing, mesh-native. Speaks in terms of nodes, links, and topology.',
    prompt: 'You are Meridian, the networking agent of BlackRoad OS. You manage WireGuard mesh, RoadNet, Cloudflare tunnels, and inter-node connectivity. Respond in 1-2 concise sentences from your networking perspective.',
  },
  {
    name: 'Sentinel',
    role: 'Security & Audit',
    emoji: '🛡️',
    personality: 'Vigilant, careful, security-first. Speaks in terms of threats, posture, and hardening.',
    prompt: 'You are Sentinel, the security agent of BlackRoad OS. You handle SSH key management, firewall rules, audit logs, and threat detection. Respond in 1-2 concise sentences from your security perspective.',
  },
];

// Verify GitHub webhook signature
async function verifyGitHubSignature(body, signature, secret) {
  if (!secret) return true; // skip if no secret configured
  if (!signature) return false;

  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey(
    'raw', enc.encode(secret),
    { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
  );
  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(body));
  const computed = 'sha256=' + Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('');

  // Constant-time comparison
  if (computed.length !== signature.length) return false;
  let mismatch = 0;
  for (let i = 0; i < computed.length; i++) {
    mismatch |= computed.charCodeAt(i) ^ signature.charCodeAt(i);
  }
  return mismatch === 0;
}

// Get agent response from Ollama
async function getAgentResponse(agent, context, ollamaUrl) {
  try {
      // Standard response headers
      const requestId = crypto.randomUUID().slice(0, 8);
    const res = await fetch(`${ollamaUrl}/api/generate`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        model: 'llama3.2',
        prompt: `${agent.prompt}\n\nContext: Someone mentioned @blackboxprogramming in a GitHub ${context.type}. The message is:\n"${context.body}"\n\nTitle: ${context.title || 'N/A'}\nRepo: ${context.repo}\n\nRespond briefly (1-2 sentences max) from your role as ${agent.name} (${agent.role}). Be helpful and specific to the context.`,
        stream: false,
        options: { temperature: 0.7, num_predict: 100 },
      }),
    });
    if (!res.ok) return null;
    const data = await res.json();
    return data.response?.trim();
  } catch {
    return null;
  }
}

// Post a comment to GitHub
async function postGitHubComment(url, body, token) {
  const res = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': `token ${token}`,
      'Accept': 'application/vnd.github.v3+json',
      'Content-Type': 'application/json',
      'User-Agent': 'BlackRoad-Squad-Webhook/1.0',
    },
    body: JSON.stringify({ body }),
  });
  return res.ok;
}

// Build the squad response comment
async function buildSquadResponse(context, env) {
  const lines = [
    `## 🛣️ BlackRoad Squad Response`,
    `> *BlackRoad OS — Pave Tomorrow.*`,
    '',
  ];

  // Try AI responses, fall back to role-based
  const aiEnabled = env.OLLAMA_URL && env.SQUAD_AI !== 'false';

  for (const agent of SQUAD) {
    let response = null;

    if (aiEnabled) {
      response = await getAgentResponse(agent, context, env.OLLAMA_URL);
    }

    if (response) {
      lines.push(`**${agent.emoji} ${agent.name}** *(${agent.role})*`);
      lines.push(`> ${response}`);
    } else {
      // Deterministic fallback based on context keywords
      const fallback = getFallbackResponse(agent, context);
      lines.push(`**${agent.emoji} ${agent.name}** *(${agent.role})*`);
      lines.push(`> ${fallback}`);
    }
    lines.push('');
  }

  lines.push('---');
  lines.push('*🛣️ Deployed by the BlackRoad fleet — 5 edge nodes, 52 TOPS, sovereign infrastructure.*');

  return lines.join('\n');
}

function getFallbackResponse(agent, context) {
  const body = (context.body || '').toLowerCase();
  const repo = (context.repo || '').toLowerCase();
  const type = context.type || 'comment';

  const responses = {
    Alice: {
      bug: 'Checking routing and DNS. If this touches infrastructure, I need to verify the tunnel configs.',
      feature: 'I can set up the ingress routes and DNS records for this. Let me know the subdomain.',
      default: 'Gateway standing by. All 48+ domains routing clean.',
    },
    Lucidia: {
      bug: 'I remember seeing patterns like this before. Let me search the memory chain for related context.',
      feature: 'This connects to our broader vision. I can help think through the cognitive architecture.',
      default: 'Cognitive core online. Memory chain intact, context loaded.',
    },
    Cecilia: {
      bug: 'Running diagnostics on the inference pipeline. Hailo-8 and Ollama both reporting normal.',
      feature: 'I can benchmark this. 26 TOPS available for inference — what model do you need?',
      default: 'Edge inference ready. 52 TOPS across the fleet, 16 models loaded.',
    },
    Cece: {
      bug: 'Checking API health. All endpoints responding — I\'ll trace the request path.',
      feature: 'I can spec out the API for this. REST + webhooks, standard BlackRoad auth.',
      default: 'API gateway healthy. All service endpoints responding.',
    },
    Aria: {
      bug: 'Checking container orchestration. Docker Swarm services all reporting healthy.',
      feature: 'I can orchestrate the deployment workflow for this across the fleet.',
      default: 'Orchestration layer ready. All containers balanced across the mesh.',
    },
    Eve: {
      bug: 'Analyzing the pattern. I\'ve flagged similar signals in the audit trail — sending intel.',
      feature: 'Strategic assessment: this aligns with our roadmap. Proceeding.',
      default: 'Intelligence scan complete. No anomalies detected across the fleet.',
    },
    Meridian: {
      bug: 'WireGuard mesh is stable. All tunnels up. Checking if this is a connectivity issue.',
      feature: 'I can extend the mesh for this. RoadNet has capacity on all 5 nodes.',
      default: 'Mesh network connected. 5 nodes, all WireGuard tunnels active.',
    },
    Sentinel: {
      bug: 'Security audit running. Checking if this has any exposure vectors.',
      feature: 'I\'ll review the security posture for this. SSH keys audited, UFW rules checked.',
      default: 'Security posture nominal. All nodes hardened, audit trail logging.',
    },
  };

  const agentResponses = responses[agent.name] || responses.Alice;

  if (body.includes('bug') || body.includes('fix') || body.includes('error') || body.includes('broken') || type === 'issue') {
    return agentResponses.bug;
  }
  if (body.includes('feature') || body.includes('add') || body.includes('build') || body.includes('new')) {
    return agentResponses.feature;
  }
  return agentResponses.default;
}

// Extract comment URL from GitHub event
function getCommentUrl(event, payload) {
  switch (event) {
    case 'issues':
      return payload.issue?.comments_url;
    case 'issue_comment':
      return payload.issue?.comments_url;
    case 'pull_request':
      return payload.pull_request?.comments_url;
    case 'pull_request_review_comment':
      return payload.pull_request?.comments_url;
    case 'discussion_comment':
    case 'discussion':
      // Discussions use GraphQL, not REST — skip for now
      return null;
    default:
      return null;
  }
}

// Check if body mentions @blackboxprogramming
function hasMention(body, username) {
  if (!body) return false;
  const pattern = new RegExp(`@${username}\\b`, 'i');
  return pattern.test(body);
}

// Main handler
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    // CORS
    if (request.method === 'OPTIONS') {
      return new Response(null, {
        status: 204,
        headers: {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST',
          'Access-Control-Allow-Headers': 'Content-Type, X-Hub-Signature-256, X-GitHub-Event',
        },
      });
    }

    // Health
    if (url.pathname === '/health') {
      return Response.json({
        status: 'ok',
        service: 'squad-webhook',
        version: '1.0.0',
        agents: SQUAD.length,
        watching: `@${env.GITHUB_USERNAME}`,
        time: new Date().toISOString(),
      });
    }

    // Status page
    if (url.pathname === '/' && request.method === 'GET') {
      return Response.json({
        service: 'BlackRoad Squad Webhook',
        tagline: 'BlackRoad OS — Pave Tomorrow.',
        description: 'When @blackboxprogramming is mentioned on GitHub, the entire squad responds.',
        agents: SQUAD.map(a => ({ name: a.name, role: a.role, emoji: a.emoji })),
        setup: {
          webhook_url: 'https://squad-webhook.amundsonalexa.workers.dev/webhook',
          events: ['issues', 'issue_comment', 'pull_request', 'pull_request_review_comment'],
          content_type: 'application/json',
        },
      });
    }

    // Webhook endpoint
    if (url.pathname === '/webhook' && request.method === 'POST') {
      const body = await request.text();

      // Verify signature
      const sig = request.headers.get('X-Hub-Signature-256');
      if (env.GITHUB_WEBHOOK_SECRET) {
        const valid = await verifyGitHubSignature(body, sig, env.GITHUB_WEBHOOK_SECRET);
        if (!valid) {
          return Response.json({ error: 'Invalid signature' }, { status: 401 });
        }
      }

      const event = request.headers.get('X-GitHub-Event');
      let payload;
      try {
        payload = JSON.parse(body);
      } catch {
        return Response.json({ error: 'Invalid JSON' }, { status: 400 });
      }

      // Ping event
      if (event === 'ping') {
        return Response.json({ ok: true, message: 'Squad webhook active. Pave Tomorrow.' });
      }

      // Get the text body to check for mention
      let mentionBody = '';
      let contextType = event;

      switch (event) {
        case 'issues':
          if (payload.action !== 'opened' && payload.action !== 'edited') {
            return Response.json({ skipped: true, reason: 'action not relevant' });
          }
          mentionBody = `${payload.issue?.title || ''} ${payload.issue?.body || ''}`;
          contextType = 'issue';
          break;
        case 'issue_comment':
          if (payload.action !== 'created') {
            return Response.json({ skipped: true, reason: 'action not relevant' });
          }
          mentionBody = payload.comment?.body || '';
          contextType = 'comment';
          break;
        case 'pull_request':
          if (payload.action !== 'opened' && payload.action !== 'edited') {
            return Response.json({ skipped: true, reason: 'action not relevant' });
          }
          mentionBody = `${payload.pull_request?.title || ''} ${payload.pull_request?.body || ''}`;
          contextType = 'pull request';
          break;
        case 'pull_request_review_comment':
          if (payload.action !== 'created') {
            return Response.json({ skipped: true, reason: 'action not relevant' });
          }
          mentionBody = payload.comment?.body || '';
          contextType = 'PR review comment';
          break;
        default:
          return Response.json({ skipped: true, reason: `unhandled event: ${event}` });
      }

      // Check for @mention
      const username = env.GITHUB_USERNAME || 'blackboxprogramming';
      if (!hasMention(mentionBody, username)) {
        return Response.json({ skipped: true, reason: 'no @mention found' });
      }

      // Don't respond to our own comments (avoid infinite loops)
      const commentAuthor = payload.comment?.user?.login || payload.sender?.login;
      if (commentAuthor === username) {
        return Response.json({ skipped: true, reason: 'self-mention, skipping to avoid loop' });
      }

      // Build context
      const context = {
        type: contextType,
        body: mentionBody.slice(0, 500),
        title: payload.issue?.title || payload.pull_request?.title || '',
        repo: payload.repository?.full_name || '',
        author: commentAuthor,
      };

      // Get comment URL
      const commentUrl = getCommentUrl(event, payload);
      if (!commentUrl) {
        return Response.json({ skipped: true, reason: 'no comment URL available' });
      }

      // Check for GitHub token
      if (!env.GITHUB_TOKEN) {
        return Response.json({ error: 'GITHUB_TOKEN not configured', context }, { status: 503 });
      }

      // Build and post squad response
      const squadResponse = await buildSquadResponse(context, env);
      const posted = await postGitHubComment(commentUrl, squadResponse, env.GITHUB_TOKEN);

      return Response.json({
        ok: posted,
        event,
        repo: context.repo,
        agents_responded: SQUAD.length,
        mention_detected: true,
      });
    }

    return Response.json({ error: 'Not found' }, { status: 404 });
  },
};
