import { createHmac, randomUUID, randomBytes } from "node:crypto";
import { decryptSecret, encryptSecret } from "@/lib/ai/crypto";
import { processAutomationRules } from "@/lib/automations/engine";
import { deliverGoogleSheetsIntegrationEvent } from "@/lib/integrations/google-sheets";
import { deliverGoogleCalendarIntegrationEvent } from "@/lib/integrations/google-calendar";
import { deliverNotionIntegrationEvent } from "@/lib/integrations/notion";
import { deliverSlackIntegrationEvent } from "@/lib/integrations/slack";
import { deliverTelegramIntegrationEvent } from "@/lib/integrations/telegram";
import { createAdminClient } from "@/lib/supabase/admin";
import type { IntegrationConnection, WebhookEndpoint, WebhookEventType } from "@/types";

export interface WebhookEventEnvelope {
  id: string;
  type: WebhookEventType;
  created_at: string;
  workspace: {
    team_id: string | null;
    user_id: string;
  };
  actor_id: string | null;
  data: Record<string, unknown>;
}

function buildTestWebhookData(eventType: WebhookEventType) {
  if (eventType.startsWith("project.")) {
    return {
      project: {
        id: "test-project-001",
        name: "Website relaunch",
        health: "on_track",
        approval_status: "approved",
      },
      source: "developer_test",
    };
  }

  if (eventType.startsWith("task.")) {
    return {
      task: {
        id: "test-task-001",
        title: "Review onboarding brief",
        status: eventType === "task.deleted" ? "done" : "in_progress",
        priority: "high",
      },
      source: "developer_test",
    };
  }

  if (eventType.startsWith("note.")) {
    return {
      note: {
        id: "test-note-001",
        title: "Client call summary",
      },
      source: "developer_test",
    };
  }

  return {
    event: {
      id: "test-event-001",
      title: "Weekly sync",
      start_time: new Date().toISOString(),
      end_time: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
    },
    source: "developer_test",
  };
}

function signWebhookPayload(secret: string, timestamp: string, payload: string) {
  return createHmac("sha256", secret).update(`${timestamp}.${payload}`).digest("hex");
}

function timeoutSignal(timeoutMs: number) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  return {
    signal: controller.signal,
    clear() {
      clearTimeout(timer);
    },
  };
}

export function createWebhookSecret() {
  return `whsec_${randomBytes(24).toString("base64url")}`;
}

export function encryptWebhookSecret(secret: string) {
  return encryptSecret(secret);
}

export async function emitWorkspaceWebhookEvent(input: {
  userId: string;
  teamId: string | null;
  actorId: string | null;
  eventType: WebhookEventType;
  data: Record<string, unknown>;
}) {
  const admin = createAdminClient();
  let endpointQuery = admin
    .from("webhook_endpoints")
    .select(
      "id, user_id, team_id, name, url, event_types, is_active, last_delivery_at, last_response_status, last_error, created_at, updated_at, signing_secret_encrypted"
    )
    .eq("is_active", true);
  let integrationQuery = admin
    .from("integration_connections")
    .select(
      "id, user_id, team_id, provider, name, config, event_types, is_active, last_delivery_at, last_response_status, last_error, created_at, updated_at, secret_encrypted"
    )
    .eq("is_active", true);

  if (input.teamId) {
    endpointQuery = endpointQuery.eq("team_id", input.teamId);
    integrationQuery = integrationQuery.eq("team_id", input.teamId);
  } else {
    endpointQuery = endpointQuery.is("team_id", null).eq("user_id", input.userId);
    integrationQuery = integrationQuery.is("team_id", null).eq("user_id", input.userId);
  }

  const [{ data: endpointRows }, { data: integrationRows }] = await Promise.all([
    endpointQuery,
    integrationQuery,
  ]);

  const endpoints = (endpointRows || []).filter((row) =>
    Array.isArray(row.event_types) ? row.event_types.includes(input.eventType) : false
  ) as Array<WebhookEndpoint & { signing_secret_encrypted: string }>;
  const integrationConnections = (integrationRows || []).filter((row) =>
    Array.isArray(row.event_types) ? row.event_types.includes(input.eventType) : false
  ) as Array<IntegrationConnection & { secret_encrypted: string }>;

  const envelope: WebhookEventEnvelope = {
    id: randomUUID(),
    type: input.eventType,
    created_at: new Date().toISOString(),
    workspace: {
      team_id: input.teamId,
      user_id: input.userId,
    },
    actor_id: input.actorId,
    data: input.data,
  };

  const payload = JSON.stringify(envelope);
  await Promise.allSettled(
    endpoints.map((endpoint) => deliverWebhook(admin, endpoint, envelope, payload))
  );
  await Promise.allSettled(
    integrationConnections.map((connection) => {
      if (connection.provider === "slack") {
        return deliverSlackIntegrationEvent(admin, connection, envelope);
      }

      if (connection.provider === "google_calendar") {
        return deliverGoogleCalendarIntegrationEvent(admin, connection, envelope);
      }

      if (connection.provider === "telegram") {
        return deliverTelegramIntegrationEvent(admin, connection, envelope);
      }

      if (connection.provider === "google_sheets") {
        return deliverGoogleSheetsIntegrationEvent(admin, connection, envelope);
      }

      if (connection.provider === "notion") {
        return deliverNotionIntegrationEvent(admin, connection, envelope);
      }

      return Promise.resolve();
    })
  );
  const automationResult = await processAutomationRules(admin, envelope);

  return {
    delivered: endpoints.length,
    integrationDelivered: integrationConnections.length,
    automationMatched: automationResult.matched,
    automationExecuted: automationResult.executed,
  };
}

async function deliverWebhook(
  admin: ReturnType<typeof createAdminClient>,
  endpoint: WebhookEndpoint & { signing_secret_encrypted: string },
  envelope: WebhookEventEnvelope,
  payload: string
) {
  const timestamp = String(Math.floor(Date.now() / 1000));
  const secret = decryptSecret(endpoint.signing_secret_encrypted);
  const signature = signWebhookPayload(secret, timestamp, payload);
  const deliveryId = randomUUID();
  const requestHeaders = {
    "Content-Type": "application/json",
    "User-Agent": "Mindavio-Webhooks/1.0",
    "X-Mindavio-Event": envelope.type,
    "X-Mindavio-Delivery": deliveryId,
    "X-Mindavio-Timestamp": timestamp,
    "X-Mindavio-Signature": `v1=${signature}`,
  };

  let responseStatus: number | null = null;
  let responseBody: string | null = null;
  let success = false;

  const { signal, clear } = timeoutSignal(8000);

  try {
    const response = await fetch(endpoint.url, {
      method: "POST",
      headers: requestHeaders,
      body: payload,
      signal,
    });

    responseStatus = response.status;
    responseBody = (await response.text()).slice(0, 4000) || null;
    success = response.ok;
  } catch (error) {
    responseBody =
      error instanceof Error ? error.message.slice(0, 4000) : "Webhook delivery failed";
  } finally {
    clear();
  }

  await admin.from("webhook_deliveries").insert({
    endpoint_id: endpoint.id,
    user_id: endpoint.user_id,
    team_id: endpoint.team_id,
    event_type: envelope.type,
    delivery_id: deliveryId,
    payload: envelope,
    response_status: responseStatus,
    response_body: responseBody,
    success,
  });

  await admin
    .from("webhook_endpoints")
    .update({
      last_delivery_at: new Date().toISOString(),
      last_response_status: responseStatus,
      last_error: success ? null : responseBody,
    })
    .eq("id", endpoint.id);
}

export async function sendTestWebhookDelivery(
  admin: ReturnType<typeof createAdminClient>,
  endpoint: WebhookEndpoint & { signing_secret_encrypted: string },
  input?: {
    eventType?: WebhookEventType;
  }
) {
  const eventType = input?.eventType || endpoint.event_types[0] || "task.created";

  const envelope: WebhookEventEnvelope = {
    id: randomUUID(),
    type: eventType,
    created_at: new Date().toISOString(),
    workspace: {
      team_id: endpoint.team_id,
      user_id: endpoint.user_id,
    },
    actor_id: endpoint.user_id,
    data: buildTestWebhookData(eventType),
  };

  const payload = JSON.stringify(envelope);
  await deliverWebhook(admin, endpoint, envelope, payload);

  return {
    eventType,
    endpointId: endpoint.id,
  };
}
