import React, { createContext, useContext } from "react";
import { useBaseStore } from "../stores/base";
import { useChatStore } from "../stores/chats";
import { Message } from "../types/discord";
import { HttpResponse, LoginResponse } from "../types/types";
import { URLSearchParams } from "react-native-url-polyfill";
import { ImageSourcePropType } from "react-native";

interface ApiContextType {
  login: (email: string, password: string, instanceUrl: string) => Promise<HttpResponse<LoginResponse>>;
  http: <T = any>(url: string, method?: "GET" | "POST", body?: unknown) => Promise<HttpResponse<T>>;
  getAvatarUrl: (id?: string, hash?: string | null) => ImageSourcePropType;
  populateMessages: (channelId: string, opts?: { before?: string; limit?: number }) => Promise<void>;
  sendMessage: (channelId: string, content: string) => Promise<HttpResponse<Message>>
}

const ApiContext = createContext<ApiContextType | null>(null);

export const ApiProvider = ({ children }: { children: React.ReactNode }) => {
  const setToken = useBaseStore((s) => s.setToken);
  const setBaseUrl = useBaseStore((s) => s.setBaseUrl);

  const token = useBaseStore((s) => s.token);
  const baseUrl = useBaseStore((s) => s.baseUrl);

  const http = async <T = any>(
    url: string,
    method: "GET" | "POST" = "GET",
    body?: unknown
  ) => {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 5000);

    const isAbsolute = /^https?:\/\//i.test(url);
    const newUrl = isAbsolute ? url : `${baseUrl}${url}`;

    try {
      const res = await fetch(newUrl, {
        method,
        headers: {
          "Content-Type": "application/json",
          ...(token ? { Authorization: `Bearer ${token}` } : {}),
        },
        body: body ? JSON.stringify(body) : undefined,
        signal: controller.signal,
      });

      const contentType = res.headers.get("content-type") || "";
      const data: T | null = contentType.includes("application/json") ? await res.json() : null;

      if (res.ok)
        return { ok: true, status: res.status, data };

      return {
        ok: false,
        status: res.status,
        data,
        error: (data as any)?.message || `HTTP ${res.status}`
      };
    } catch (err: any) {
      const error = err.name === "AbortError" ? "timeout" : err?.message ?? String(err);
      return { ok: false, status: 0, data: null, error };
    } finally {
      clearTimeout(timeout);
    }
  };

  const login = async (
    email: string,
    password: string,
    instanceUrl: string
  ) => {
    const res = await http<LoginResponse>(
      `${instanceUrl}/api/v9/auth/login`,
      "POST",
      { login: email, password }
    );
    if (res.ok && res.data?.token) {
      setToken(res.data.token);
      setBaseUrl(instanceUrl);
    }
    return res;
  };

  const populateMessages = async (
    channelId: string,
    opts?: { before?: string; limit?: number }
  ) => {
    const { before, limit = 50 } = opts ?? {};

    const params = new URLSearchParams();
    if (before) params.set("before", before);
    if (limit) params.set("limit", String(limit));

    const query = params.toString();
    const endpoint = `/api/v9/channels/${channelId}/messages${query ? `?${query}` : ""}`;

    const res = await http<Message[]>(endpoint, "GET");
    if (!res.ok || !res.data)
      throw new Error(res.error ?? `Failed to fetch messages for channel ${channelId}`);

    const fetched: Message[] = res.data ?? [];
    useChatStore.getState().appendOlder(channelId, fetched);
  };

  const sendMessage = async (
    channelId: string,
    content: string
  ) => {
    const nonce = Math.floor(Math.random() * 10 ** 8) + "";
    const res = await http<Message>(
      `/api/v9/channels/${channelId}/messages`,
      "POST",
      { content, nonce, tts: false }
    );
    return res;
  }

  const getAvatarUrl = (id?: string, hash?: string | null): ImageSourcePropType => {
    if (id && hash) {
      return { uri: `${baseUrl}/avatars/${id}/${hash}` };
    } else {
      return require("../../../assets/default.png");
    }
  };

  return (
    <ApiContext.Provider
      value={{
        http,
        login,
        getAvatarUrl,
        populateMessages,
        sendMessage,
      }}
    >
      {children}
    </ApiContext.Provider>
  );
};

export const useApi = () => {
  const ctx = useContext(ApiContext);
  if (!ctx) throw new Error("useApi must be used inside ApiProvider");
  return ctx;
};