"use client";

import { useCallback, useEffect, useState } from "react";
import { useWorkspaceStore } from "@/stores/workspace-store";
import type {
  DeveloperAccessLevel,
  DeveloperApiToken,
  WebhookDelivery,
  WebhookEndpoint,
  WebhookEventType,
} from "@/types";
import type { TokenScopes } from "@/lib/developers/scopes";

interface CreateTokenInput {
  name: string;
  accessLevel: DeveloperAccessLevel;
  scopes?: TokenScopes;
  expiresAt?: string | null;
}

interface CreateWebhookInput {
  name: string;
  url: string;
  eventTypes: WebhookEventType[];
}

export function useDeveloperPortal() {
  const activeTeamId = useWorkspaceStore((state) => state.activeTeamId);
  const initialized = useWorkspaceStore((state) => state.initialized);
  const [tokens, setTokens] = useState<DeveloperApiToken[]>([]);
  const [webhooks, setWebhooks] = useState<WebhookEndpoint[]>([]);
  const [deliveriesByWebhook, setDeliveriesByWebhook] = useState<Record<string, WebhookDelivery[]>>(
    {}
  );
  const [loading, setLoading] = useState(true);

  const teamQuery = activeTeamId ? `?teamId=${encodeURIComponent(activeTeamId)}` : "";

  const fetchAll = useCallback(async () => {
    if (!initialized) return;

    setLoading(true);
    const [tokensRes, webhooksRes] = await Promise.all([
      fetch(`/api/developers/tokens${teamQuery}`),
      fetch(`/api/developers/webhooks${teamQuery}`),
    ]);

    const tokensJson = tokensRes.ok ? await tokensRes.json() : { tokens: [] };
    const webhooksJson = webhooksRes.ok ? await webhooksRes.json() : { webhooks: [] };

    setTokens(tokensJson.tokens || []);
    setWebhooks(webhooksJson.webhooks || []);
    setLoading(false);
  }, [initialized, teamQuery]);

  useEffect(() => {
    const timer = window.setTimeout(() => {
      void fetchAll();
    }, 0);

    return () => window.clearTimeout(timer);
  }, [fetchAll]);

  async function createToken(input: CreateTokenInput) {
    const res = await fetch("/api/developers/tokens", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        teamId: activeTeamId,
        name: input.name,
        accessLevel: input.accessLevel,
        scopes: input.scopes ?? null,
        expiresAt: input.expiresAt ? new Date(input.expiresAt).toISOString() : null,
      }),
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "API token could not be created");
    }

    setTokens((prev) => [json.token, ...prev]);
    return json as { token: DeveloperApiToken; tokenValue: string; tokenHint: string };
  }

  async function updateTokenScopes(id: string, scopes: TokenScopes) {
    const res = await fetch(`/api/developers/tokens/${id}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ scopes }),
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "Token rights could not be changed");
    }

    setTokens((prev) => prev.map((token) => (token.id === id ? { ...token, ...json.token } : token)));
    return json.token as DeveloperApiToken;
  }

  async function revokeToken(id: string) {
    const res = await fetch(`/api/developers/tokens/${id}`, {
      method: "DELETE",
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "API token could not be revoked");
    }

    setTokens((prev) =>
      prev.map((token) =>
        token.id === id
          ? {
              ...token,
              revoked_at: new Date().toISOString(),
            }
          : token
      )
    );
  }

  async function createWebhook(input: CreateWebhookInput) {
    const res = await fetch("/api/developers/webhooks", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        teamId: activeTeamId,
        name: input.name,
        url: input.url,
        eventTypes: input.eventTypes,
      }),
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "Webhook could not be created");
    }

    setWebhooks((prev) => [json.webhook, ...prev]);
    return json as { webhook: WebhookEndpoint; signingSecret: string };
  }

  async function updateWebhook(
    id: string,
    input: Partial<{ name: string; url: string; eventTypes: WebhookEventType[]; isActive: boolean }>
  ) {
    const res = await fetch(`/api/developers/webhooks/${id}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(input),
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "Webhook could not be updated");
    }

    setWebhooks((prev) => prev.map((webhook) => (webhook.id === id ? json.webhook : webhook)));
    return json.webhook as WebhookEndpoint;
  }

  async function deleteWebhook(id: string) {
    const res = await fetch(`/api/developers/webhooks/${id}`, {
      method: "DELETE",
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "Webhook could not be deleted");
    }

    setWebhooks((prev) => prev.filter((webhook) => webhook.id !== id));
    setDeliveriesByWebhook((prev) => {
      const next = { ...prev };
      delete next[id];
      return next;
    });
  }

  async function fetchDeliveries(webhookId: string) {
    const res = await fetch(`/api/developers/webhooks/${webhookId}/deliveries?limit=20`);
    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "Deliveries could not be loaded");
    }

    setDeliveriesByWebhook((prev) => ({
      ...prev,
      [webhookId]: json.deliveries || [],
    }));
  }

  async function sendTestWebhook(webhookId: string, eventType?: WebhookEventType) {
    const res = await fetch(`/api/developers/webhooks/${webhookId}/test`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ eventType }),
    });

    const json = await res.json();
    if (!res.ok) {
      throw new Error(json.error || "Test webhook could not be sent");
    }

    await fetchDeliveries(webhookId);
    await fetchAll();
    return json as { success: true; eventType: WebhookEventType; endpointId: string };
  }

  return {
    activeTeamId,
    tokens,
    webhooks,
    deliveriesByWebhook,
    loading,
    createToken,
    updateTokenScopes,
    revokeToken,
    createWebhook,
    updateWebhook,
    deleteWebhook,
    fetchDeliveries,
    sendTestWebhook,
    refetch: fetchAll,
  };
}
