export const dynamic = 'force-dynamic';
export const revalidate = 0;

import { NextResponse } from 'next/server';
import crypto from 'crypto';
import { getSql } from '../../../../lib/db.js';
import { getOrgId } from '../../../../lib/org.js';
import { apiErrorResponse } from '../../../../lib/apiErrors.js';
import { evaluateGuard } from '../../../../lib/guard.js';
import {
  createActionRecord,
  createBlockedActionRecord,
} from '../../../../lib/repositories/actions.repository.js';
import { scanSensitiveData } from '../../../../lib/security.js';
import { RISK_SCORE_MAP } from '../../../../lib/capability-invoke.js';
import {
  executeCapabilityInvocation,
  prepareCapabilityInvocation,
} from '../../../../lib/capability-runtime.js';
import { checkQuotaFast, getOrgPlan, incrementMeter } from '../../../../lib/usage.js';
import { checkCircuitBreaker } from '../../../../lib/capability-health.js';
import { updateCapability } from '../../../../lib/repositories/capabilities.repository.js';
import { evaluateAccess } from '../../../../lib/repositories/capability-access.repository.js';

function redactAny(value, findings) {
  if (typeof value === 'string') {
    const scan = scanSensitiveData(value);
    if (!scan.clean) findings.push(...scan.findings);
    return scan.redacted;
  }
  if (Array.isArray(value)) return value.map((v) => redactAny(v, findings));
  if (value && typeof value === 'object') {
    const out = {};
    for (const [k, v] of Object.entries(value)) out[k] = redactAny(v, findings);
    return out;
  }
  return value;
}

export async function POST(request, { params }) {
  try {
    const { capabilityId } = await params;
    const sql = getSql();
    const orgId = getOrgId(request);
    const body = await request.json();

    // 1. Load and resolve capability runtime config
    let prepared;
    try {
      prepared = await prepareCapabilityInvocation(sql, orgId, capabilityId);
    } catch (err) {
      if (err.message === `Capability not found: ${capabilityId}`) {
        return NextResponse.json(
          { success: false, error: 'capability_not_found' },
          { status: 404 },
        );
      }

      if (err.message === `Capability ${capabilityId} is not an http_api type`) {
        return NextResponse.json(
          { success: false, error: 'not_invocable', message: 'Capability is not invocable via HTTP' },
          { status: 400 },
        );
      }

      if (err.code === 'auth_not_configured') {
        return NextResponse.json(
          { success: false, error: 'auth_not_configured', message: err.message },
          { status: 400 },
        );
      }

      if (err.code === 'endpoint_not_configured') {
        return NextResponse.json(
          { success: false, error: 'endpoint_not_configured', message: err.message },
          { status: 400 },
        );
      }

      if (err.code === 'capability_contract_invalid') {
        return NextResponse.json(
          { success: false, error: 'capability_contract_invalid', message: err.message },
          { status: 400 },
        );
      }

      throw err;
    }

    const { capability, schema } = prepared;
    if (!capability) {
      return NextResponse.json(
        { success: false, error: 'capability_not_found' },
        { status: 404 },
      );
    }
    const action_id = `act_${crypto.randomUUID()}`;
    const timestamp_start = new Date().toISOString();

    // 2. Guard evaluation
    const riskScore = RISK_SCORE_MAP[capability.risk_level] || 50;
    const guardDecision = await evaluateGuard(
      orgId,
      {
        action_type: 'capability_invoke',
        risk_score: riskScore,
        agent_id: body.agent_id || null,
        systems_touched: [`capability:${capability.slug}`],
        reversible: true,
        declared_goal: body.declared_goal || `Invoke capability: ${capability.name}`,
      },
      sql,
    );

    // 3. DLP scan on input
    const dlpFindings = [];
    const inputSummary = redactAny(
      JSON.stringify(body).slice(0, 500),
      dlpFindings,
    );

    const actionData = {
      agent_id: body.agent_id || 'anonymous',
      action_type: 'capability_invoke',
      declared_goal: body.declared_goal || `Invoke capability: ${capability.name}`,
      systems_touched: [`capability:${capability.slug}`],
      reversible: true,
      risk_score: riskScore,
      confidence: 50,
      input_summary: inputSummary,
    };

    // 4. Handle guard blocked
    if (guardDecision.decision === 'block') {
      await createBlockedActionRecord(sql, {
        orgId,
        action_id,
        data: actionData,
        guardDecision,
        signature: null,
        verified: false,
        timestamp_start,
      });

      return NextResponse.json(
        {
          success: false,
          error: 'blocked_by_policy',
          guard_decision: {
            decision: guardDecision.decision,
            reasons: guardDecision.reasons || [],
            matched_policies: guardDecision.matched_policies || [],
          },
        },
        { status: 403 },
      );
    }

    // 5. Handle require_approval
    if (guardDecision.decision === 'require_approval' || capability.requires_approval) {
      await createActionRecord(sql, {
        orgId,
        action_id,
        data: { ...actionData, status: 'pending_approval' },
        actionStatus: 'pending_approval',
        costEstimate: 0,
        signature: null,
        verified: false,
        timestamp_start,
      });

      return NextResponse.json(
        {
          success: false,
          error: 'pending_approval',
          action_id,
          message: `Invocation requires human approval. Poll /api/approvals/${action_id} for status.`,
        },
        { status: 202 },
      );
    }

    // Quota check
    const plan = await getOrgPlan(orgId, sql);
    const capQuota = await checkQuotaFast(orgId, 'capability_invocations', plan, sql);
    if (!capQuota.allowed) {
      return NextResponse.json(
        {
          success: false,
          error: 'quota_exceeded',
          code: 'QUOTA_EXCEEDED',
          resource: 'capability_invocations',
          usage: capQuota.usage,
          limit: capQuota.limit,
          message: 'Monthly capability invocation limit exceeded. Upgrade your plan to continue.',
          upgrade_url: '/billing',
        },
        { status: 402 },
      );
    }

    // Circuit breaker check
    const circuitStatus = await checkCircuitBreaker(sql, orgId, capability);
    if (circuitStatus.open) {
      return NextResponse.json(
        {
          success: false,
          error: 'circuit_breaker_open',
          message: `Capability circuit breaker is open after ${circuitStatus.consecutive_failures} consecutive failures. Run a test to reset.`,
          consecutive_failures: circuitStatus.consecutive_failures,
        },
        { status: 503 },
      );
    }

    // Access control check
    const agentId = body.agent_id || 'anonymous';
    const accessResult = await evaluateAccess(sql, orgId, capabilityId, agentId);
    if (accessResult.access === 'deny') {
      return NextResponse.json({
        success: false,
        error: 'access_denied',
        code: 'CAPABILITY_ACCESS_DENIED',
        reason: accessResult.rule?.reason || 'Agent does not have access to this capability.',
        capability_id: capabilityId,
        agent_id: agentId,
      }, { status: 403 });
    }
    if (accessResult.access === 'require_approval') {
      await createActionRecord(sql, {
        orgId,
        action_id,
        data: { ...actionData, status: 'pending_approval' },
        actionStatus: 'pending_approval',
        costEstimate: 0,
        signature: null,
        verified: false,
        timestamp_start,
      });

      return NextResponse.json(
        {
          success: false,
          error: 'pending_approval',
          action_id,
          message: `Invocation requires human approval. Poll /api/approvals/${action_id} for status.`,
          reason: accessResult.rule?.reason || null,
        },
        { status: 202 },
      );
    }

    // 6. Create running action record
    await createActionRecord(sql, {
      orgId,
      action_id,
      data: actionData,
      actionStatus: 'running',
      costEstimate: capability.pricing?.estimated_cost_usd || 0,
      signature: null,
      verified: false,
      timestamp_start,
    });

    // 7. Invoke the capability
    const result = await executeCapabilityInvocation({
      endpoint: prepared.endpoint,
      authHeaders: prepared.authHeaders,
      schema,
      body,
    });

    // 8. Update action outcome
    const timestamp_end = new Date().toISOString();
    const retryPrefix = result.retry_metadata?.retried
      ? `[retried: ${result.retry_metadata.total_attempts} attempts] `
      : '';
    const outputSummary = result.success
      ? retryPrefix + JSON.stringify(result.data).slice(0, 500 - retryPrefix.length)
      : retryPrefix + (result.message || result.error);

    await sql`
      UPDATE action_records
      SET status = ${result.success ? 'completed' : 'failed'},
          output_summary = ${outputSummary},
          error_message = ${result.success ? null : result.message || result.error},
          timestamp_end = ${timestamp_end},
          duration_ms = ${result.elapsed_ms || 0}
      WHERE action_id = ${action_id} AND org_id = ${orgId}
    `;

    // Keep health_status in step with the invocation outcome so checkCircuitBreaker
    // can actually count consecutive failures — without this, the 'healthy' short-circuit
    // in capability-health.js prevents the breaker from ever opening.
    {
      const nextHealth = result.success ? 'healthy' : 'degraded';
      void updateCapability(sql, orgId, capabilityId, { health_status: nextHealth })
        .catch((err) => console.warn('[API] Health status update failed:', err.message));
    }

    // 9. Return response
    if (!result.success) {
      const statusCode = result.error === 'capability_timeout'
        ? 504
        : result.error === 'capability_input_invalid'
          ? 400
          : 502;
      return NextResponse.json(
        {
          success: false,
          action_id,
          error: result.error,
          message: result.message,
          elapsed_ms: result.elapsed_ms,
          governed: true,
          retry_metadata: result.retry_metadata || undefined,
        },
        { status: statusCode },
      );
    }

    // Meter increment (fire-and-forget)
    void Promise.all([
      incrementMeter(orgId, 'capability_invocations', sql),
      incrementMeter(orgId, 'governed_actions', sql),
    ]).catch((err) => console.warn('[API] Meter increment failed:', err.message));

    return NextResponse.json({
      success: true,
      action_id,
      result: result.data,
      elapsed_ms: result.elapsed_ms,
      governed: true,
      retry_metadata: result.retry_metadata || undefined,
      quota_warning: capQuota.warning || undefined,
      security: {
        clean: dlpFindings.length === 0,
        findings_count: dlpFindings.length,
        critical_count: dlpFindings.filter((f) => f.severity === 'critical').length,
        categories: [...new Set(dlpFindings.map((f) => f.category))],
      },
    });
  } catch (error) {
    return apiErrorResponse(error, 'CAPABILITY_INVOKE');
  }
}
