"use client";

/**
 * Single-fetch anatomy hook shared by the Product / Hosting tabs.
 * Hardened against silent failure: 10s timeout, error surfacing, and
 * graceful unmount.
 *
 * Uses SWR to deduplicate requests across components (ProjectStagePill,
 * ProjectHeaderUrls, Hosting page) so they share a single network request
 * instead of hammering the API independently.
 */

import useSWR from "swr";
import { useCallback } from "react";

export interface Anatomy {
  project: {
    id: string;
    name: string;
    gitea?: string;
    coolifyProjectUuid?: string;
  };
  codebasesReason?: "no_repo" | "empty_repo";
  product: {
    codebases: Array<{
      id: string;
      label: string;
      path: string;
      hint?: string;
    }>;
    images: Array<{
      uuid: string;
      name: string;
      image: string;
      version: string;
      serviceType?: string;
      status?: string;
    }>;
  };
  hosting: {
    live: Array<{
      uuid: string;
      name: string;
      source: "repo" | "image";
      sourceLabel: string;
      status: string;
      fqdn?: string;
      domains: string[];
      branch?: string;
      buildPack?: string;
      lastBuild?: {
        status: string;
        createdAt?: string;
        finishedAt?: string;
        commit?: string;
      };
      inFlightBuild?: {
        status: string;
        createdAt?: string;
        finishedAt?: string;
        commit?: string;
      };
    }>;
    previews: Array<{
      id: string;
      name: string;
      command?: string;
      port: number;
      url: string;
      state: string;
      startedAt: string;
    }>;
  };
  infrastructure: {
    databases: Array<{
      uuid: string;
      name: string;
      type: string;
      status: string;
      isPublic: boolean;
      publicPort?: number;
      internalAddress?: string;
      consumerEnvKey: string;
    }>;
    providers: Array<{
      id: string;
      category: "auth" | "email" | "payments" | "llm" | "storage";
      vendor: string;
      attachments: Array<{
        resourceUuid: string;
        resourceName: string;
        resourceKind: "app" | "service";
        keys: string[];
      }>;
    }>;
    bundledStorage: {
      status: "ready" | "pending" | "partial" | "error" | "unprovisioned";
      bucketName?: string;
      hmacAccessId?: string;
      region?: string;
      errorMessage?: string;
    };
    secrets: {
      total: number;
      byResource: Array<{
        resourceUuid: string;
        resourceName: string;
        resourceKind: "app" | "service";
        count: number;
        keys: string[];
      }>;
    };
  };
}

export interface UseAnatomyResult {
  anatomy: Anatomy | null;
  loading: boolean;
  error: string | null;
  reload: () => void;
}

export interface UseAnatomyOptions {
  /** When set, re-fetch anatomy every N ms while the component is
   *  mounted. Used by the project-header status pill so it surfaces
   *  Coolify build state transitions live (e.g. queued → in_progress
   *  → success) without the user having to refresh. Pass undefined or
   *  0 to disable polling. */
  pollMs?: number;
}

const fetcher = async (url: string) => {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 10_000);
  try {
    const res = await fetch(url, {
      credentials: "include",
      signal: controller.signal,
      cache: "no-store",
    });
    let body: unknown = {};
    try {
      body = await res.json();
    } catch {
      /* keep {} */
    }
    if (!res.ok) {
      const msg =
        (body as { error?: string }).error ||
        `HTTP ${res.status} ${res.statusText}`.trim();
      throw new Error(msg);
    }
    return body as Anatomy;
  } catch (err: any) {
    if (err.name === "AbortError") {
      throw new Error("Request timed out after 10s.");
    }
    throw err;
  } finally {
    clearTimeout(timeout);
  }
};

export function useAnatomy(
  projectId: string,
  options: UseAnatomyOptions = {},
): UseAnatomyResult {
  const pollMs = options.pollMs && options.pollMs > 0 ? options.pollMs : 0;

  const { data, error, isLoading, mutate } = useSWR<Anatomy, Error>(
    projectId ? `/api/projects/${projectId}/anatomy` : null,
    fetcher,
    {
      refreshInterval: pollMs,
      dedupingInterval: 5000,
      revalidateOnFocus: false,
      revalidateOnReconnect: false,
    },
  );

  const reload = useCallback(() => {
    mutate();
  }, [mutate]);

  return {
    anatomy: data ?? null,
    loading: isLoading,
    error: error?.message ?? null,
    reload,
  };
}
