const LOCAL_STORAGE_KEY = "lcelive_session";
const DEFAULT_BASE_URL = "https://api.lcelive.co.uk";
const MAX_REFRESH_RETRIES = 1;
import { TauriService } from "./TauriService";
export interface LceLiveAccount {
  accountId: string;
  username: string;
  displayName: string;
}

export interface SessionData {
  accessToken: string;
  refreshToken: string;
  account: LceLiveAccount;
}

export interface DeviceLinkStartResponse {
  deviceCode: string;
  userCode: string;
  verificationUri: string;
  verificationUriComplete?: string;
  intervalSeconds: number;
  expiresInSeconds?: number;
}

export interface DeviceLinkPollResponse {
  status: string;
  isLinked?: boolean;
  accessToken?: string;
  refreshToken?: string;
  account?: LceLiveAccount;
}

export interface GameInvite {
  inviteId: string;
  from: LceLiveAccount | string;
  hostIp: string;
  hostPort: number;
  hostName: string;
  signalingSessionId?: string;
  status: string;
}

export interface FriendRequest {
  accountId: string;
  username: string;
  displayName: string;
}

export interface PendingRequests {
  incoming: FriendRequest[];
  outgoing: FriendRequest[];
}

export class LceLiveService {
  private _session: SessionData | null = null;
  private baseUrl: string = DEFAULT_BASE_URL;
  private _refreshPromise: Promise<void> | null = null;

  constructor() {
    this.loadSession();
  }

  setBaseUrl(url: string) {
    this.baseUrl = url.replace(/\/$/, "");
  }

  get signedIn(): boolean {
    return this._session !== null;
  }

  get account(): LceLiveAccount | null {
    return this._session?.account || null;
  }

  get displayUsername(): string {
    if (!this._session) return "Not signed in";
    return (
      this._session.account.displayName ||
      this._session.account.username ||
      "Unknown"
    );
  }

  get apiBaseUrl(): string {
    return this.baseUrl;
  }

  get accessToken(): string | null {
    return this._session?.accessToken || null;
  }

  public generateDeviceId(): string {
    let id = localStorage.getItem("lcelive_device_id");
    if (!id) {
      id = crypto.randomUUID();
      localStorage.setItem("lcelive_device_id", id);
    }
    return id;
  }

  public getDeviceName(): string {
    return "LCE Emerald Launcher";
  }

  private loadSession() {
    try {
      const data = localStorage.getItem(LOCAL_STORAGE_KEY);
      if (data) {
        this._session = JSON.parse(data);
      }
    } catch (e) {
      console.warn("Failed to load LceLive session", e);
    }
  }

  private saveSession() {
    if (this._session) {
      localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this._session));
    } else {
      localStorage.removeItem(LOCAL_STORAGE_KEY);
    }
  }

  private async request<T = any>(
    method: string,
    path: string,
    body?: unknown,
    authed: boolean = true,
    retryCount: number = 0,
  ): Promise<T> {
    if (authed && this._session?.refreshToken && retryCount === 0) {
      try {
        await this.refreshSession(); //neo: i do this on every request only because it doesnt always return 401
      } catch (err) {
        this.logoutLocal();
        throw new Error("Session expired. Please log in again.");
      }
    }

    const headers: Record<string, string> = {
      Accept: "application/json",
      "User-Agent": "MCLCE-LceLive/1.0",
    };

    if (body) {
      headers["Content-Type"] = "application/json";
    }

    if (authed && this._session?.accessToken) {
      headers["Authorization"] = `Bearer ${this._session.accessToken}`;
    }

    const bodyStr = body ? JSON.stringify(body) : null;

    let res;
    try {
      res = await TauriService.httpProxyRequest(
        method,
        `${this.baseUrl}${path}`,
        bodyStr,
        headers,
      );
    } catch (e) {
      throw new Error(`Network error when calling ${path}: ${e}`);
    }

    if (
      res.status === 401 &&
      authed &&
      this._session?.refreshToken &&
      retryCount < MAX_REFRESH_RETRIES
    ) {
      try {
        await this.refreshSession();
        return this.request(method, path, body, authed, retryCount + 1);
      } catch (err) {
        this.logoutLocal();
        throw new Error("Session expired. Please log in again.");
      }
    }

    let data;
    try {
      data = res.body ? JSON.parse(res.body) : {};
    } catch {
      data = { message: res.body };
    }

    if (res.status >= 400) {
      const errorMsg =
        data.message ||
        data.detail ||
        data.title ||
        data.error ||
        `HTTP ${res.status}`;
      throw new Error(errorMsg);
    }

    return data;
  }

  async startDeviceLink(): Promise<DeviceLinkStartResponse> {
    return this.request<DeviceLinkStartResponse>(
      "POST",
      "/api/auth/device/start",
      {
        deviceId: this.generateDeviceId(),
        deviceName: this.getDeviceName(),
      },
      false,
    );
  }

  async pollDeviceLink(deviceCode: string): Promise<DeviceLinkPollResponse> {
    const data = await this.request<DeviceLinkPollResponse>(
      "GET",
      `/api/auth/device/poll/${deviceCode}`,
      null,
      false,
    );
    if (data.isLinked && data.accessToken) {
      this._session = {
        accessToken: data.accessToken,
        refreshToken: data.refreshToken ?? "",
        account: data.account ?? {
          accountId: "",
          username: "",
          displayName: "",
        },
      };
      this.saveSession();
    }
    return data;
  }

  async refreshSession(): Promise<void> {
    if (!this._session?.refreshToken) throw new Error("No refresh token");

    if (this._refreshPromise) {
      return this._refreshPromise;
    }

    this._refreshPromise = (async () => {
      try {
        const data = await this.request(
          "POST",
          "/api/auth/refresh",
          {
            refreshToken: this._session!.refreshToken,
          },
          false,
        );
        this._session = {
          accessToken: data.accessToken,
          refreshToken: data.refreshToken,
          account: data.account,
        };
        this.saveSession();
      } finally {
        this._refreshPromise = null;
      }
    })();

    return this._refreshPromise;
  }

  async logout(): Promise<void> {
    if (this._session?.refreshToken) {
      try {
        await this.request(
          "POST",
          "/api/auth/logout",
          {
            refreshToken: this._session.refreshToken,
          },
          true,
        );
      } catch (e) {
        console.warn("Server logout failed", e);
      }
    }
    this.logoutLocal();
  }

  logoutLocal(): void {
    this._session = null;
    this.saveSession();
  }

  async getFriends(): Promise<LceLiveAccount[]> {
    const data = await this.request("GET", "/api/social/friends");
    return data.friends || [];
  }

  async removeFriend(accountId: string): Promise<void> {
    await this.request("DELETE", `/api/social/friends/${accountId}`);
  }

  async sendFriendRequest(username: string): Promise<void> {
    await this.request("POST", "/api/social/request", { username });
  }

  async getPendingRequests(): Promise<PendingRequests> {
    const data = await this.request("GET", "/api/social/requests");
    return {
      incoming: (data.incoming || []).map((r: Record<string, string>) => ({
        accountId: r.requesterUserId || r.accountId || r.userId,
        username: r.requesterUsername || r.username,
        displayName: r.requesterDisplayName || r.displayName,
      })),
      outgoing: (data.outgoing || []).map((r: Record<string, string>) => ({
        accountId: r.targetUserId || r.accountId || r.userId,
        username: r.targetUsername || r.username,
        displayName: r.targetDisplayName || r.displayName,
      })),
    };
  }

  async acceptFriendRequest(accountId: string): Promise<void> {
    await this.request("POST", `/api/social/requests/${accountId}/accept`, {});
  }

  async declineFriendRequest(accountId: string): Promise<void> {
    await this.request("POST", `/api/social/requests/${accountId}/decline`, {});
  }

  async getGameInvites(): Promise<GameInvite[]> {
    const data = await this.request("GET", "/api/sessions/invites");
    const incoming = data.incoming || [];
    return incoming.map((inv: Record<string, unknown>) => ({
      inviteId: inv.inviteId,
      from: {
        accountId: inv.senderAccountId,
        username: inv.senderUsername,
        displayName: inv.senderDisplayName,
      },
      hostIp: inv.hostIp,
      hostPort: inv.hostPort,
      hostName: inv.hostName,
      status: inv.status,
      signalingSessionId: inv.signalingSessionId,
    }));
  }

  async sendGameInvite(
    recipientAccountId: string,
    hostIp: string,
    hostPort: number,
    hostName: string,
    signalingSessionId?: string,
  ): Promise<void> {
    await this.request("POST", "/api/sessions/invites", {
      recipientAccountId,
      hostIp,
      hostPort,
      hostName,
      signalingSessionId,
    });
  }

  async acceptGameInvite(inviteId: string): Promise<Record<string, unknown>> {
    return this.request("POST", `/api/sessions/invites/${inviteId}/accept`, {});
  }

  async declineGameInvite(inviteId: string): Promise<void> {
    await this.request("POST", `/api/sessions/invites/${inviteId}/decline`, {});
  }

  async deactivateGameInvites(): Promise<void> {
    await this.request("POST", "/api/sessions/invites/deactivate", {});
  }

  async requestJoinTicket(): Promise<string> {
    const data = await this.request("POST", "/api/sessions/ticket", {});
    return data.ticket;
  }

  async validateJoinTicket(ticket: string): Promise<LceLiveAccount> {
    return this.request("POST", "/api/sessions/validate", { ticket }, false);
  }
}

export const lceLiveService = new LceLiveService();
