import { importPkcs8Ed25519, signBase64 } from './crypto-utils.js';
import { WsJsonClient } from './ws-client.js';

const SESSION_TYPE_WALLET = 50;

function normalizeServerUrl(url) {
  const value = String(url || '').trim();
  if (!value) return 'wss://shineup.me/ws';
  if (value.startsWith('ws://') || value.startsWith('wss://')) return value;
  if (value.startsWith('http://') || value.startsWith('https://')) {
    const parsed = new URL(value);
    parsed.protocol = parsed.protocol === 'https:' ? 'wss:' : 'ws:';
    if (!parsed.pathname || parsed.pathname === '/') parsed.pathname = '/ws';
    return parsed.toString();
  }
  return value;
}

function opError(op, response) {
  const payload = response?.payload || {};
  const message = payload?.message || response?.message || payload?.error || response?.error || 'Unknown server error';
  const code = String(payload?.code || response?.code || payload?.error || response?.error || 'UNKNOWN').toUpperCase();
  const error = new Error(`${op}: ${message} (${code})`);
  error.op = op;
  error.code = code;
  error.status = response?.status || 0;
  return error;
}

export class ShineApiClient {
  constructor(serverUrl) {
    this.serverUrl = normalizeServerUrl(serverUrl);
    this.ws = new WsJsonClient(this.serverUrl);
  }

  async getUser(login) {
    const response = await this.ws.request('GetUser', { login: String(login || '').trim() });
    if (response.status !== 200) throw opError('GetUser', response);
    return response.payload || {};
  }

  async startTrustedDeviceLogin({ login, passwordHash, requesterSessionKey, payloadType = 1 }) {
    const response = await this.ws.request('StartTrustedDeviceLogin', {
      login: String(login || '').trim(),
      passwordHash: String(passwordHash || '').trim(),
      requesterSessionKey: String(requesterSessionKey || '').trim(),
      requesterSessionType: SESSION_TYPE_WALLET,
      requesterClientPlatform: 'Chrome Extension Wallet',
      payloadType: Number(payloadType) || 1,
    });
    if (response.status !== 200) throw opError('StartTrustedDeviceLogin', response);
    return response.payload || {};
  }

  async getTrustedDeviceLoginStatus(pairingId) {
    const response = await this.ws.request('GetTrustedDeviceLoginStatus', {
      pairingId: String(pairingId || '').trim(),
    });
    if (response.status !== 200) throw opError('GetTrustedDeviceLoginStatus', response);
    return response.payload || {};
  }

  async cancelTrustedDeviceLogin(pairingId, requesterSessionKey) {
    const response = await this.ws.request('CancelTrustedDeviceLogin', {
      pairingId: String(pairingId || '').trim(),
      requesterSessionKey: String(requesterSessionKey || '').trim(),
    });
    if (response.status !== 200) throw opError('CancelTrustedDeviceLogin', response);
    return response.payload || {};
  }

  async listSessions() {
    const response = await this.ws.request('ListSessions', {});
    if (response.status !== 200) throw opError('ListSessions', response);
    return Array.isArray(response?.payload?.sessions) ? response.payload.sessions : [];
  }

  async callSignalToSession({ toLogin, targetSessionId, callId, type, data = '' }) {
    const response = await this.ws.request('CallSignalToSession', {
      toLogin: String(toLogin || '').trim(),
      targetSessionId: String(targetSessionId || '').trim(),
      callId: String(callId || '').trim(),
      type: Number(type) || 0,
      data: String(data || ''),
    });
    if (response.status !== 200) throw opError('CallSignalToSession', response);
    return response.payload || {};
  }

  onEvent(op, handler) {
    return this.ws.on(op, handler);
  }

  async resumeSession(sessionRecord) {
    const login = String(sessionRecord?.login || '').trim();
    const sessionId = String(sessionRecord?.sessionId || '').trim();
    const sessionKey = String(sessionRecord?.sessionKey || '').trim();
    const sessionPrivPkcs8 = String(sessionRecord?.sessionPrivPkcs8 || '').trim();
    if (!login || !sessionId || !sessionKey || !sessionPrivPkcs8) {
      throw new Error('Сохранённая wallet-session неполная');
    }

    const privateKey = await importPkcs8Ed25519(sessionPrivPkcs8);
    const challengeResp = await this.ws.request('SessionChallenge', { sessionId });
    if (challengeResp.status !== 200) throw opError('SessionChallenge', challengeResp);
    const nonce = challengeResp?.payload?.nonce;
    if (!nonce) throw new Error('SessionChallenge: сервер не вернул nonce');

    const timeMs = Date.now();
    const preimage = `SESSION_LOGIN:${sessionId}:${timeMs}:${nonce}`;
    const signatureB64 = await signBase64(privateKey, preimage);
    const loginResp = await this.ws.request('SessionLogin', {
      sessionId,
      sessionKey,
      timeMs,
      signatureB64,
      sessionType: Number(sessionRecord?.sessionType || SESSION_TYPE_WALLET) || SESSION_TYPE_WALLET,
      clientPlatform: 'Chrome Extension Wallet',
      clientInfo: 'SHiNE Browser Plugin Wallet',
    });
    if (loginResp.status !== 200) throw opError('SessionLogin', loginResp);

    return {
      login,
      sessionId,
      storagePwd: String(loginResp?.payload?.storagePwd || '').trim(),
    };
  }

  close() {
    this.ws.close();
  }
}
