import { createRequestId } from '../../../shared/request-id.js';

const AUTH_STORAGE_PREFIX = 'invttrdg_web';
const ACCESS_TOKEN_KEY = `${AUTH_STORAGE_PREFIX}_access_token`;
const REFRESH_TOKEN_KEY = `${AUTH_STORAGE_PREFIX}_refresh_token`;
const USER_KEY = `${AUTH_STORAGE_PREFIX}_auth_user`;
const AUTH_CHANGE_EVENT = 'trading-platform-auth-change';

export interface PlatformSessionUser {
  id: string;
  email?: string;
  role?: string;
  plan?: string;
  display_name?: string;
  user_metadata?: Record<string, unknown>;
}

export interface PlatformSession {
  access_token: string;
  refresh_token: string;
  user: PlatformSessionUser;
}

class PlatformAuthError extends Error {
  status?: number;

  constructor(message: string, status?: number) {
    super(message);
    this.name = 'PlatformAuthError';
    this.status = status;
  }
}

function parseJson<T>(value: string | null): T | null {
  if (!value) return null;
  try {
    return JSON.parse(value) as T;
  } catch {
    return null;
  }
}

function getStorage(): Storage | null {
  if (typeof window === 'undefined') return null;
  const storage = window.localStorage;
  if (!storage) return null;
  if (
    typeof storage.getItem !== 'function'
    || typeof storage.setItem !== 'function'
    || typeof storage.removeItem !== 'function'
  ) {
    return null;
  }
  return storage;
}

export function getStoredPlatformSession(): PlatformSession | null {
  const storage = getStorage();
  if (!storage) return null;
  const accessToken = storage.getItem(ACCESS_TOKEN_KEY);
  const refreshToken = storage.getItem(REFRESH_TOKEN_KEY);
  const user = parseJson<PlatformSessionUser>(storage.getItem(USER_KEY));
  if (!accessToken || !refreshToken || !user?.id) {
    return null;
  }
  return {
    access_token: accessToken,
    refresh_token: refreshToken,
    user,
  };
}

function savePlatformSession(session: PlatformSession): void {
  const storage = getStorage();
  if (!storage) return;
  storage.setItem(ACCESS_TOKEN_KEY, session.access_token);
  storage.setItem(REFRESH_TOKEN_KEY, session.refresh_token);
  storage.setItem(USER_KEY, JSON.stringify(session.user));
}

export function clearPlatformSession(): void {
  const storage = getStorage();
  if (!storage) return;
  storage.removeItem(ACCESS_TOKEN_KEY);
  storage.removeItem(REFRESH_TOKEN_KEY);
  storage.removeItem(USER_KEY);
}

export function emitPlatformAuthChange(event: string, session: PlatformSession | null): void {
  if (typeof window === 'undefined') return;
  window.dispatchEvent(new CustomEvent(AUTH_CHANGE_EVENT, { detail: { event, session } }));
}

export function subscribePlatformAuthChange(
  callback: (event: string, session: PlatformSession | null) => void
): () => void {
  if (typeof window === 'undefined') {
    return () => {};
  }

  const handler = (rawEvent: Event) => {
    const event = rawEvent as CustomEvent<{ event?: string; session?: PlatformSession | null }>;
    callback(String(event.detail?.event || 'UNKNOWN'), event.detail?.session ?? null);
  };

  window.addEventListener(AUTH_CHANGE_EVENT, handler as EventListener);
  return () => {
    window.removeEventListener(AUTH_CHANGE_EVENT, handler as EventListener);
  };
}

function decodeJwtPayload(token: string): Record<string, any> | null {
  try {
    const [, payload] = token.split('.');
    if (!payload) return null;
    return JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
  } catch {
    return null;
  }
}

function isAccessTokenFresh(token: string): boolean {
  const claims = decodeJwtPayload(token);
  const exp = Number(claims?.exp || 0);
  if (!exp) return false;
  return exp > Math.floor(Date.now() / 1000) + 60;
}

function normalizeUser(input: any): PlatformSessionUser {
  return {
    id: String(input?.id || input?.sub || '').trim(),
    email: typeof input?.email === 'string' ? input.email : undefined,
    role: typeof input?.role === 'string' ? input.role : undefined,
    plan: typeof input?.plan === 'string' ? input.plan : undefined,
    display_name: typeof input?.displayName === 'string' ? input.displayName : undefined,
    user_metadata: {
      role: input?.role,
      plan: input?.plan,
      displayName: input?.displayName,
    },
  };
}

async function platformRequest<T>(
  path: string,
  options?: {
    method?: string;
    accessToken?: string;
    body?: Record<string, unknown>;
  }
): Promise<T> {
  const runtimeModule = await import('./runtime');
  const response = await fetch(`${runtimeModule.tradingRuntime.platformApiUrl}${path}`, {
    method: options?.method || 'GET',
    headers: {
      'Content-Type': 'application/json',
      'x-product-id': runtimeModule.tradingRuntime.productId,
      'x-request-id': createRequestId('web-auth'),
      ...(options?.accessToken ? { Authorization: `Bearer ${options.accessToken}` } : {}),
    },
    body: options?.body ? JSON.stringify(options.body) : undefined,
  });

  const payload = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new PlatformAuthError(
      String((payload as { message?: string; error?: string }).message || (payload as { error?: string }).error || `HTTP ${response.status}`),
      response.status
    );
  }

  return payload as T;
}

async function getPlatformUser(accessToken: string): Promise<PlatformSessionUser> {
  const me = await platformRequest<any>('/auth/me', { accessToken });
  return normalizeUser(me);
}

async function refreshPlatformSession(refreshToken: string): Promise<PlatformSession> {
  const refreshed = await platformRequest<{ accessToken: string; refreshToken: string }>('/auth/refresh', {
    method: 'POST',
    body: { refreshToken },
  });
  const user = await getPlatformUser(refreshed.accessToken);
  const nextSession: PlatformSession = {
    access_token: refreshed.accessToken,
    refresh_token: refreshed.refreshToken,
    user,
  };
  savePlatformSession(nextSession);
  return nextSession;
}

export async function ensurePlatformSession(): Promise<PlatformSession | null> {
  const stored = getStoredPlatformSession();
  if (!stored) {
    return null;
  }

  if (isAccessTokenFresh(stored.access_token) && stored.user?.id) {
    return stored;
  }

  try {
    const user = await getPlatformUser(stored.access_token);
    const nextSession = { ...stored, user };
    savePlatformSession(nextSession);
    return nextSession;
  } catch (error) {
    if ((error as PlatformAuthError)?.status === 401 || (error as PlatformAuthError)?.status === 403) {
      try {
        const refreshed = await refreshPlatformSession(stored.refresh_token);
        emitPlatformAuthChange('TOKEN_REFRESHED', refreshed);
        return refreshed;
      } catch {
        clearPlatformSession();
        emitPlatformAuthChange('SIGNED_OUT', null);
        return null;
      }
    }
    throw error;
  }
}

export async function getPlatformAccessToken(): Promise<string> {
  const session = await ensurePlatformSession();
  const accessToken = session?.access_token;
  if (!accessToken) {
    throw new Error('Not authenticated');
  }
  return accessToken;
}

export function getPlatformAccessTokenSync(): string {
  const session = getStoredPlatformSession();
  const accessToken = session?.access_token;
  if (!accessToken) {
    throw new Error('Not authenticated');
  }
  return accessToken;
}

function getPasswordResetToken(): string | null {
  if (typeof window === 'undefined') return null;
  const url = new URL(window.location.href);
  const directToken = url.searchParams.get('token');
  if (directToken) return directToken;

  const hashParams = new URLSearchParams(url.hash.replace(/^#/, ''));
  return hashParams.get('token');
}

export async function resetPlatformPassword(newPassword: string): Promise<void> {
  const token = getPasswordResetToken();
  if (!token) {
    throw new Error('Missing password reset token');
  }
  await platformRequest('/auth/reset-password', {
    method: 'POST',
    body: {
      token,
      newPassword,
    },
  });
}
