import { invoke } from "@tauri-apps/api/core";
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
import { getApiBase } from "@/utils/api-base";

const API_BASE = getApiBase();
const DESKTOP_AUTH_POLL_INTERVAL_MS = 1500;
const DESKTOP_AUTH_TIMEOUT_MS = 5 * 60 * 1000;
const DESKTOP_SESSION_SECRET_HEADER = "X-Desktop-Session-Secret";
let authTokenCache: string | null | undefined;

interface DesktopAuthApiOptions {
  apiBase?: string;
}

export interface AuthUser {
  id: number;
  email: string;
  name: string | null;
  avatar_url: string | null;
  provider: string | null;
  github_username: string | null;
  subscription_status: "free" | "pro";
  created_at: string;
}

export interface SubscriptionInfo {
  status: "free" | "pro";
  subscription: {
    plan: string;
    renews_at: string | null;
    ends_at: string | null;
  } | null;
  enterprise: {
    has_access: boolean;
    is_admin: boolean;
    policy: {
      managedMode: boolean;
      requireExtensionAllowlist: boolean;
      allowedExtensionIds: string[];
      allowByok: boolean;
      aiCompletionEnabled: boolean;
      aiChatEnabled: boolean;
      updatedAt: string | null;
    } | null;
  };
  autocomplete?: {
    usage?: Record<string, unknown> | null;
  } | null;
}

export interface EnterprisePolicy {
  managedMode: boolean;
  requireExtensionAllowlist: boolean;
  allowedExtensionIds: string[];
  allowByok: boolean;
  aiCompletionEnabled: boolean;
  aiChatEnabled: boolean;
  updatedAt: string | null;
}

export interface CloudSettingsSyncSnapshot {
  schemaVersion: number;
  updatedAt: string;
  settings: Record<string, unknown>;
}

type DesktopAuthPollResponse =
  | { status: "pending" }
  | { status: "ready"; token: string }
  | { status: "expired" }
  | { status: "missing" };

type DesktopAuthInitResponse = {
  sessionId?: unknown;
  pollSecret?: unknown;
  loginUrl?: unknown;
};

export class DesktopAuthError extends Error {
  code: "endpoint_unavailable" | "expired" | "timeout" | "failed";

  constructor(code: DesktopAuthError["code"], message: string) {
    super(message);
    this.name = "DesktopAuthError";
    this.code = code;
  }
}

export class AuthApiError extends Error {
  status: number;

  constructor(message: string, status: number) {
    super(message);
    this.name = "AuthApiError";
    this.status = status;
  }
}

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

export function isAuthInvalidError(error: unknown): boolean {
  return error instanceof AuthApiError && (error.status === 401 || error.status === 403);
}

function getApiBaseUnavailableMessage(apiBase = API_BASE): string {
  if (apiBase.includes("localhost") || apiBase.includes("127.0.0.1")) {
    return `Could not reach local auth server at ${apiBase}. Start the local web app/server first, then try sign-in again.`;
  }

  return `Could not reach auth server at ${apiBase}.`;
}

// Secure token storage via Rust backend
export const getAuthToken = async (): Promise<string | null> => {
  if (authTokenCache !== undefined) {
    return authTokenCache;
  }

  try {
    authTokenCache = await invoke<string | null>("get_auth_token");
    return authTokenCache;
  } catch {
    return null;
  }
};

export const storeAuthToken = async (token: string): Promise<void> => {
  await invoke("store_auth_token", { token });
  authTokenCache = token;
};

export const removeAuthToken = async (): Promise<void> => {
  authTokenCache = null;
  await invoke("remove_auth_token");
};

// Authenticated API fetch helper
async function authenticatedFetch(
  path: string,
  options: RequestInit = {},
  tokenOverride?: string,
): Promise<Response> {
  const token = tokenOverride ?? (await getAuthToken());
  if (!token) {
    throw new Error("Not authenticated");
  }

  return tauriFetch(`${API_BASE}${path}`, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
      ...options.headers,
    },
  });
}

export async function fetchCurrentUser(tokenOverride?: string): Promise<AuthUser> {
  const response = await authenticatedFetch("/api/auth/me", {}, tokenOverride);
  if (!response.ok) {
    throw new AuthApiError(`Failed to fetch user: ${response.status}`, response.status);
  }
  const data = await response.json();
  if (!data.user) {
    throw new AuthApiError("Authentication token did not resolve to a user.", 401);
  }
  return data.user;
}

export async function fetchSubscriptionStatus(tokenOverride?: string): Promise<SubscriptionInfo> {
  const response = await authenticatedFetch("/api/auth/subscription", {}, tokenOverride);
  if (!response.ok) {
    throw new AuthApiError(`Failed to fetch subscription: ${response.status}`, response.status);
  }
  return await response.json();
}

export async function updateEnterprisePolicy(
  patch: Partial<Omit<EnterprisePolicy, "updatedAt">>,
): Promise<EnterprisePolicy> {
  const response = await authenticatedFetch("/api/enterprise/policy", {
    method: "PATCH",
    body: JSON.stringify(patch),
  });

  const payload = (await response.json().catch(() => null)) as {
    policy?: EnterprisePolicy;
    error?: string;
  } | null;

  if (!response.ok || !payload?.policy) {
    throw new Error(payload?.error || `Failed to update enterprise policy: ${response.status}`);
  }

  return payload.policy;
}

export async function fetchSettingsSyncSnapshot(
  tokenOverride?: string,
): Promise<CloudSettingsSyncSnapshot | null> {
  const response = await authenticatedFetch("/api/account/settings-sync", {}, tokenOverride);
  if (!response.ok) {
    throw new AuthApiError(
      `Failed to fetch settings sync snapshot: ${response.status}`,
      response.status,
    );
  }

  const data = (await response.json()) as {
    snapshot?: CloudSettingsSyncSnapshot | null;
  };
  return data.snapshot ?? null;
}

export async function pushSettingsSyncSnapshot(input: {
  schemaVersion: number;
  settings: Record<string, unknown>;
}): Promise<CloudSettingsSyncSnapshot> {
  const response = await authenticatedFetch("/api/account/settings-sync", {
    method: "PUT",
    body: JSON.stringify(input),
  });

  const data = (await response.json().catch(() => null)) as {
    snapshot?: CloudSettingsSyncSnapshot;
    error?: string;
  } | null;

  if (!response.ok || !data?.snapshot) {
    throw new AuthApiError(
      data?.error || `Failed to update settings sync snapshot: ${response.status}`,
      response.status,
    );
  }

  return data.snapshot;
}

export async function logoutFromServer(): Promise<void> {
  try {
    await authenticatedFetch("/api/auth/logout", { method: "DELETE" });
  } catch {
    // Even if server logout fails, we still clear the local token
  }
}

export async function beginDesktopAuthSession(options: DesktopAuthApiOptions = {}): Promise<{
  sessionId: string;
  pollSecret: string;
  loginUrl: string;
}> {
  const apiBase = options.apiBase ?? API_BASE;
  let response: Response;
  try {
    response = await tauriFetch(`${apiBase}/api/auth/desktop/session/init`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
    });
  } catch (error) {
    throw new DesktopAuthError(
      "failed",
      error instanceof Error
        ? `${getApiBaseUnavailableMessage(apiBase)} ${error.message}`
        : getApiBaseUnavailableMessage(apiBase),
    );
  }

  if (response.status === 404) {
    throw new DesktopAuthError(
      "endpoint_unavailable",
      "Desktop auth session endpoint is unavailable on this server.",
    );
  }

  if (!response.ok) {
    throw new DesktopAuthError(
      "failed",
      `Failed to initialize desktop sign-in (${response.status}).`,
    );
  }

  const payload = (await response.json()) as DesktopAuthInitResponse;
  const parsed = parseDesktopAuthInitResponse(payload);
  if (!parsed) {
    throw new DesktopAuthError("failed", "Invalid desktop sign-in initialization response.");
  }

  return parsed;
}

function parseDesktopAuthInitResponse(payload: unknown): {
  sessionId: string;
  pollSecret: string;
  loginUrl: string;
} | null {
  if (!payload || typeof payload !== "object") return null;
  const candidate = payload as {
    sessionId?: unknown;
    pollSecret?: unknown;
    loginUrl?: unknown;
  };

  if (
    typeof candidate.sessionId !== "string" ||
    typeof candidate.pollSecret !== "string" ||
    typeof candidate.loginUrl !== "string"
  ) {
    return null;
  }

  if (!candidate.sessionId || !candidate.pollSecret || !candidate.loginUrl) {
    return null;
  }

  return {
    sessionId: candidate.sessionId,
    pollSecret: candidate.pollSecret,
    loginUrl: candidate.loginUrl,
  };
}

function parseDesktopAuthPollResponse(payload: unknown): DesktopAuthPollResponse | null {
  if (!payload || typeof payload !== "object") return null;
  const status = (payload as { status?: unknown }).status;
  if (status === "pending" || status === "expired" || status === "missing") {
    return { status };
  }
  if (status === "ready") {
    const token = (payload as { token?: unknown }).token;
    if (typeof token === "string" && token.length > 0) {
      return { status: "ready", token };
    }
  }
  return null;
}

export async function waitForDesktopAuthToken(
  sessionId: string,
  pollSecret: string,
  timeoutMs = DESKTOP_AUTH_TIMEOUT_MS,
  options: DesktopAuthApiOptions = {},
): Promise<string> {
  const apiBase = options.apiBase ?? API_BASE;
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    const url = `${apiBase}/api/auth/desktop/session?session=${encodeURIComponent(sessionId)}`;
    let response: Response;
    try {
      response = await tauriFetch(url, {
        method: "GET",
        headers: {
          [DESKTOP_SESSION_SECRET_HEADER]: pollSecret,
        },
      });
    } catch (error) {
      throw new DesktopAuthError(
        "failed",
        error instanceof Error
          ? `${getApiBaseUnavailableMessage(apiBase)} ${error.message}`
          : getApiBaseUnavailableMessage(apiBase),
      );
    }

    if (response.status === 404) {
      throw new DesktopAuthError(
        "endpoint_unavailable",
        "Desktop auth session endpoint is unavailable on this server.",
      );
    }

    if (response.status === 410) {
      throw new DesktopAuthError("expired", "Desktop sign-in session expired.");
    }

    if (!response.ok) {
      throw new DesktopAuthError("failed", `Desktop sign-in failed (${response.status}).`);
    }

    const payload = await response.json();
    const parsed = parseDesktopAuthPollResponse(payload);

    if (!parsed) {
      throw new DesktopAuthError("failed", "Invalid desktop sign-in response.");
    }

    if (parsed.status === "ready") {
      return parsed.token;
    }

    if (parsed.status === "expired") {
      throw new DesktopAuthError("expired", "Desktop sign-in session is no longer valid.");
    }

    if (parsed.status === "missing") {
      throw new DesktopAuthError(
        "failed",
        "Desktop sign-in session credentials are invalid or the session has expired.",
      );
    }

    await sleep(DESKTOP_AUTH_POLL_INTERVAL_MS);
  }

  throw new DesktopAuthError("timeout", "Desktop sign-in timed out. Please try again.");
}

export const __test__ = {
  parseDesktopAuthInitResponse,
  parseDesktopAuthPollResponse,
  getApiBaseUnavailableMessage,
};
