import { z } from 'zod';
import type { McpToolRequest } from '../tools/types.js';
import {
  telemetryClusters,
  telemetryUpdateCluster,
  telemetryListPolicies,
  telemetryDeletePolicy,
  telemetryQuery,
} from '../../lib/platform-client.js';

// ── Handoff schemas ────────────────────────────────────────────────
export const PostIncidentCleanupInputSchema = z.object({
  productId: z.string().min(1).describe('Product ID to clean up'),
  incidentTimeWindow: z
    .object({
      from: z.string().datetime().describe('Incident start time (ISO datetime)'),
      to: z.string().datetime().describe('Incident end time (ISO datetime)'),
    })
    .describe('Time window of the incident'),
  clusterStatus: z
    .enum(['resolved', 'ignored'])
    .optional()
    .default('resolved')
    .describe('Status to set on related clusters'),
  deletePolicies: z
    .boolean()
    .optional()
    .default(true)
    .describe('Whether to delete telemetry policies created during incident'),
  exportAuditLog: z
    .boolean()
    .optional()
    .default(true)
    .describe('Whether to export audit log for the incident window'),
  dryRun: z
    .boolean()
    .optional()
    .default(false)
    .describe('If true, only report what would be done without making changes'),
});

export const PostIncidentCleanupResultSchema = z.object({
  runId: z.string(),
  productId: z.string(),
  timeWindow: z.object({
    from: z.string(),
    to: z.string(),
    durationMinutes: z.number(),
  }),
  clustersUpdated: z.number(),
  policiesDeleted: z.number(),
  auditLogExported: z.boolean(),
  auditLogSize: z.number().optional(),
  dryRun: z.boolean(),
  summary: z.string(),
});

export type PostIncidentCleanupInput = z.infer<typeof PostIncidentCleanupInputSchema>;
export type PostIncidentCleanupResult = z.infer<typeof PostIncidentCleanupResultSchema>;

// ── Pipeline implementation ───────────────────────────────────────────
export async function runPostIncidentCleanupPipeline(
  input: PostIncidentCleanupInput,
  req: McpToolRequest
): Promise<PostIncidentCleanupResult> {
  const runId = `post-incident-cleanup-${Date.now()}`;
  const { productId, incidentTimeWindow, clusterStatus, deletePolicies, exportAuditLog, dryRun } =
    input;

  const durationMinutes = Math.round(
    (new Date(incidentTimeWindow.to).getTime() - new Date(incidentTimeWindow.from).getTime()) /
      (1000 * 60)
  );

  req.log.info(
    { runId, productId, timeWindow: incidentTimeWindow, durationMinutes, dryRun },
    'Starting post-incident cleanup pipeline'
  );

  try {
    let clustersUpdated = 0;
    let policiesDeleted = 0;
    let auditLogExported = false;
    let auditLogSize: number | undefined;

    // Step 1: Find and update clusters in the incident time window
    const clustersResponse = await telemetryClusters(
      {
        platform: productId,
        from: incidentTimeWindow.from,
        to: incidentTimeWindow.to,
      },
      { token: req.headers.authorization?.replace('Bearer ', '') || '', requestId: req.id }
    );

    const clusters = clustersResponse.clusters || [];

    for (const cluster of clusters) {
      if (dryRun) {
        req.log.info(
          { runId, clusterId: cluster.id, pk: cluster.pk, currentStatus: cluster.status },
          '[DRY RUN] Would update cluster status'
        );
        clustersUpdated++;
        continue;
      }

      await telemetryUpdateCluster(cluster.id, cluster.pk, clusterStatus, {
        token: req.headers.authorization?.replace('Bearer ', '') || '',
        requestId: req.id,
      });

      clustersUpdated++;

      req.log.info(
        { runId, clusterId: cluster.id, pk: cluster.pk, newStatus: clusterStatus },
        'Updated cluster status'
      );
    }

    // Step 2: Delete telemetry policies created during incident
    if (deletePolicies) {
      const policiesResponse = await telemetryListPolicies({
        token: req.headers.authorization?.replace('Bearer ', '') || '',
        requestId: req.id,
      });

      const policies =
        (policiesResponse as { policies?: Array<{ createdAt: string; id: string; name: string }> })
          .policies || [];
      const incidentPolicies = policies.filter(
        (policy: { createdAt: string; id: string; name: string }) => {
          const policyCreatedAt = new Date(policy.createdAt);
          return (
            policyCreatedAt >= new Date(incidentTimeWindow.from) &&
            policyCreatedAt <= new Date(incidentTimeWindow.to)
          );
        }
      );

      for (const policy of incidentPolicies) {
        if (dryRun) {
          req.log.info(
            { runId, policyId: policy.id, policyName: policy.name },
            '[DRY RUN] Would delete policy'
          );
          policiesDeleted++;
          continue;
        }

        try {
          await telemetryDeletePolicy(policy.id, {
            token: req.headers.authorization?.replace('Bearer ', '') || '',
            requestId: req.id,
          });

          policiesDeleted++;

          req.log.info(
            { runId, policyId: policy.id, policyName: policy.name },
            'Deleted telemetry policy'
          );
        } catch (policyError) {
          req.log.warn(
            {
              runId,
              policyId: policy.id,
              policyName: policy.name,
              error: policyError instanceof Error ? policyError.message : String(policyError),
            },
            'Failed to delete telemetry policy (continuing)'
          );
          // Continue with next policy instead of failing entire pipeline
        }
      }
    }

    // Step 3: Export audit log for the incident window
    if (exportAuditLog) {
      if (dryRun) {
        req.log.info({ runId, timeWindow: incidentTimeWindow }, '[DRY RUN] Would export audit log');
        auditLogExported = true;
        auditLogSize = 0;
      } else {
        try {
          const auditResponse = await telemetryQuery(
            {
              productId,
              eventType: 'audit',
              from: incidentTimeWindow.from,
              to: incidentTimeWindow.to,
              limit: 10000,
            },
            { token: req.headers.authorization?.replace('Bearer ', '') || '', requestId: req.id }
          );

          const auditEvents = (auditResponse as { events?: unknown[] }).events || [];
          auditLogExported = true;
          auditLogSize = JSON.stringify(auditEvents).length;

          req.log.info(
            { runId, eventCount: auditEvents.length, sizeBytes: auditLogSize },
            'Exported audit log'
          );
        } catch (auditError) {
          req.log.warn(
            {
              runId,
              timeWindow: incidentTimeWindow,
              error: auditError instanceof Error ? auditError.message : String(auditError),
            },
            'Failed to export audit log (continuing)'
          );
          // Continue without audit log instead of failing entire pipeline
        }
      }
    }

    const summary = dryRun
      ? `[DRY RUN] Would update ${clustersUpdated} clusters to "${clusterStatus}", delete ${policiesDeleted} policies, and export audit log (${auditLogSize || 0} bytes).`
      : `Updated ${clustersUpdated} clusters to "${clusterStatus}", deleted ${policiesDeleted} policies, and exported audit log (${auditLogSize || 0} bytes).`;

    return {
      runId,
      productId,
      timeWindow: {
        from: incidentTimeWindow.from,
        to: incidentTimeWindow.to,
        durationMinutes,
      },
      clustersUpdated,
      policiesDeleted,
      auditLogExported,
      auditLogSize,
      dryRun,
      summary,
    };
  } catch (error) {
    req.log.error(
      { runId, productId, error: error instanceof Error ? error.message : String(error) },
      'Post-incident cleanup pipeline failed'
    );

    return {
      runId,
      productId,
      timeWindow: {
        from: incidentTimeWindow.from,
        to: incidentTimeWindow.to,
        durationMinutes,
      },
      clustersUpdated: 0,
      policiesDeleted: 0,
      auditLogExported: false,
      dryRun,
      summary: `Pipeline failed: ${error instanceof Error ? error.message : String(error)}`,
    };
  }
}
