import { useSessionStore } from "@/stores/session-store";
import type {
  CreateStreamParticleRequest,
  CreateStreamRequest,
  Human,
  MarkSeenBatchRequest,
  PrepareUploadRequest,
  PrepareUploadResponse,
  RequestCodeRequest,
  SignInRequest,
  SignInResponse,
  StartupResponse,
  Stream,
  StreamParticle,
} from "./types";

export class ApiError extends Error {
  constructor(
    public status: number,
    message: string,
  ) {
    super(message);
    this.name = "ApiError";
  }
}

interface ApiClientConfig {
  baseUrl: string;
  getToken: () => string | null;
  onUnauthorized: () => void;
}

class ApiClient {
  private config: ApiClientConfig;

  constructor(config: ApiClientConfig) {
    this.config = config;
  }

  private async request<T>(
    method: string,
    path: string,
    body?: unknown,
  ): Promise<T> {
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
    };

    const token = this.config.getToken();
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }

    const response = await fetch(`${this.config.baseUrl}${path}`, {
      method,
      headers,
      body: body ? JSON.stringify(body) : undefined,
    });

    if (response.status === 401) {
      this.config.onUnauthorized();
      throw new ApiError(401, "Unauthorized");
    }

    if (!response.ok) {
      const text = await response.text().catch(() => "Unknown error");
      throw new ApiError(response.status, text);
    }

    if (response.status === 204) {
      return undefined as T;
    }

    return response.json() as Promise<T>;
  }

  // --- Auth ---

  async requestCode(data: RequestCodeRequest): Promise<void> {
    await this.request<void>("POST", "/auth/request-code", data);
  }

  async signIn(data: SignInRequest): Promise<SignInResponse> {
    return this.request<SignInResponse>("POST", "/auth/sign-in", data);
  }

  async me(): Promise<Human> {
    return this.request<Human>("GET", "/auth/me");
  }

  async signOut(): Promise<void> {
    await this.request<void>("POST", "/auth/sign-out");
  }

  // --- Startup ---

  async startup(): Promise<StartupResponse> {
    return this.request<StartupResponse>("GET", "/startup");
  }

  // --- Streams ---

  async createStream(
    networkId: string,
    data: CreateStreamRequest,
  ): Promise<Stream> {
    return this.request<Stream>(
      "POST",
      `/networks/${networkId}/streams`,
      data,
    );
  }

  async createStreamParticle(
    streamId: string,
    data: CreateStreamParticleRequest,
  ): Promise<StreamParticle> {
    return this.request<StreamParticle>(
      "POST",
      `/streams/${streamId}/particles`,
      data,
    );
  }

  // --- Particles ---

  async markSeen(particleId: string): Promise<void> {
    await this.request<void>("POST", `/particles/${particleId}/seen`);
  }

  async ackParticle(particleId: string): Promise<void> {
    await this.request<void>("POST", `/particles/${particleId}/ack`);
  }

  async markSeenBatch(data: MarkSeenBatchRequest): Promise<void> {
    await this.request<void>("POST", "/particles/seen", data);
  }

  async getParticleDownloadUrl(particleId: string): Promise<string> {
    const token = this.config.getToken();
    const headers: Record<string, string> = {};
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }

    const response = await fetch(
      `${this.config.baseUrl}/particles/${particleId}/download`,
      { headers, redirect: "follow" },
    );

    if (response.status === 401) {
      this.config.onUnauthorized();
      throw new ApiError(401, "Unauthorized");
    }

    if (!response.ok) {
      throw new ApiError(response.status, "Failed to get download URL");
    }

    return response.url;
  }

  // --- Depot ---

  async prepareUpload(
    data: PrepareUploadRequest,
  ): Promise<PrepareUploadResponse> {
    return this.request<PrepareUploadResponse>("POST", "/depot/upload", data);
  }

  async confirmUpload(objectId: string): Promise<void> {
    await this.request<void>("POST", `/depot/objects/${objectId}/confirm`);
  }
}

export const apiClient = new ApiClient({
  baseUrl: "https://orion.dev.flowy.live",
  getToken: () => useSessionStore.getState().token,
  onUnauthorized: () => useSessionStore.getState().clearToken(),
});
