const ISO_NOW = () => new Date().toISOString();

export class BackendApiClient {
  constructor({ apiUrl, companionSessionToken, userAuthToken, companionVersion, platform, log }) {
    this.apiUrl = apiUrl ? apiUrl.replace(/\/+$/, '') : '';
    this.companionSessionToken = companionSessionToken;
    this.userAuthToken = userAuthToken;
    this.companionVersion = companionVersion || 'unknown';
    this.platform = platform || process.platform;
    this.log = log;
  }

  async getOrCreateLiveMatch(matchId, context = {}) {
    const response = await this.post('/live-match-status', {
      provider: 'autodarts_ws',
      externalMatchId: matchId,
      matchUrl: context.matchUrl || null,
      state: 'capturing',
      eventCount: 0,
      lastEventAt: ISO_NOW(),
      officialResultSubmission: false
    });

    return {
      id: response.liveMatchId || response.live_match_id || `external:${matchId}`,
      autodarts_match_id: matchId,
      status: response.status || 'ready',
      metadata: {
        source: 'addc_companion_backend_api',
        provider: 'autodarts_ws',
        official_result_submission: false,
        notifications_sent: false,
        standings_updated: false
      }
    };
  }

  async insertLiveMatchEvent(liveMatchId, normalized) {
    const body = {
      provider: normalized.providerName,
      liveMatchId,
      externalMatchId: normalized.externalMatchId,
      eventType: normalized.storageEventType,
      normalizedEventType: normalized.normalizedEventType,
      confidenceStatus: normalized.confidenceStatus,
      normalized: {
        eventPayload: normalized.normalizedPayload,
        scoreChange: normalized.scoreChange,
        legChange: normalized.legChange,
        playerMapping: normalized.playerMapping
      },
      rawPayload: normalized.rawPayload,
      mappingErrors: normalized.errors,
      occurredAt: ISO_NOW(),
      officialResultSubmission: false
    };

    this.log?.('POST /live-match-event request', {
      liveMatchId,
      externalMatchId: normalized.externalMatchId,
      eventType: normalized.storageEventType,
      normalizedEventType: normalized.normalizedEventType,
      confidenceStatus: normalized.confidenceStatus,
      mappingErrorCount: normalized.errors.length,
      hasRawPayload: Boolean(normalized.rawPayload && Object.keys(normalized.rawPayload).length > 0)
    });

    await this.post('/live-match-event', body);
  }

  async updateLiveMatch(liveMatch, normalized) {
    const isCompleted = normalized.normalizedEventType === 'match_completed';
    const isMappableLiveEvent = normalized.canStoreEvent && normalized.storageEventType !== 'error' && normalized.confidenceStatus !== 'failed';
    const alreadyFinished = ['completed', 'closed'].includes(liveMatch.status);
    const status = isCompleted ? 'completed' : alreadyFinished ? liveMatch.status : 'capturing';
    const ignoreStartingSnapshot = shouldIgnoreStartingScoreSnapshot(liveMatch, normalized);
    if (!isCompleted && (!isMappableLiveEvent || alreadyFinished)) {
      return {
        ...liveMatch,
        metadata: {
          ...(liveMatch.metadata || {}),
          source: 'addc_companion_backend_api',
          provider: normalized.providerName,
          last_ignored_event: normalized.storageEventType,
          mapping_confidence: normalized.confidenceStatus,
          official_result_submission: false,
          notifications_sent: false,
          standings_updated: false,
          bridge_updated_at: ISO_NOW()
        }
      };
    }
    if (ignoreStartingSnapshot) {
      this.log?.('Ignored stale Autodarts starting score snapshot', {
        liveMatchId: liveMatch.id,
        externalMatchId: normalized.externalMatchId,
        currentHomeScore: liveMatch.home_score,
        currentAwayScore: liveMatch.away_score
      });
    }
    const response = await this.post('/live-match-status', {
      provider: normalized.providerName,
      liveMatchId: liveMatch.id,
      externalMatchId: normalized.externalMatchId,
      state: status,
      scoreChange: ignoreStartingSnapshot ? withoutRemainingScores(normalized.scoreChange) : normalized.scoreChange,
      legChange: normalized.legChange,
      playerMapping: normalized.playerMapping,
      confidenceStatus: normalized.confidenceStatus,
      lastEventType: normalized.storageEventType,
      lastEventAt: ISO_NOW(),
      officialResultSubmission: false
    });

    return {
      ...liveMatch,
      id: response.liveMatchId || response.live_match_id || liveMatch.id,
      status: response.status || status,
      home_score: !ignoreStartingSnapshot && Number.isInteger(normalized.scoreChange.home_score) ? normalized.scoreChange.home_score : liveMatch.home_score,
      away_score: !ignoreStartingSnapshot && Number.isInteger(normalized.scoreChange.away_score) ? normalized.scoreChange.away_score : liveMatch.away_score,
      metadata: {
        ...(liveMatch.metadata || {}),
        source: 'addc_companion_backend_api',
        provider: normalized.providerName,
        last_event: normalized.storageEventType,
        mapping_confidence: normalized.confidenceStatus,
        official_result_submission: false,
        notifications_sent: false,
        standings_updated: false,
        bridge_updated_at: ISO_NOW()
      }
    };
  }

  async sendHeartbeat(payload = {}) {
    return await this.post('/companion-heartbeat', {
      companionVersion: this.companionVersion,
      platform: this.platform,
      status: payload.status || 'waiting',
      browserHelperConnected: Boolean(payload.browserHelperConnected),
      activeMatchId: payload.activeMatchId || null,
      lastEventAt: payload.lastEventAt || null,
      eventCount: payload.eventCount || 0
    });
  }

  async post(path, body) {
    if (!this.apiUrl) throw new Error('ADDC_BACKEND_API_URL is required in backend_api mode.');
    if (!this.companionSessionToken && !this.userAuthToken) {
      throw new Error('ADDC_COMPANION_SESSION_TOKEN or ADDC_USER_AUTH_TOKEN is required in backend_api mode.');
    }

    if (path === '/live-match-event') {
      this.log?.('backend live event POST started', {
        path,
        liveMatchId: body.liveMatchId,
        externalMatchId: body.externalMatchId,
        eventType: body.eventType,
        normalizedEventType: body.normalizedEventType
      });
    }

    const response = await fetch(`${this.apiUrl}${path}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json',
        ...this.authHeaders()
      },
      body: JSON.stringify(body)
    });

    const text = await response.text();
    const json = parseJsonResponse(text);
    if (path === '/live-match-event') {
      this.log?.('backend response status', {
        path,
        status: response.status,
        ok: response.ok,
        requestId: json.requestId || null,
        error: json.error || null,
        code: json.code || null
      });
    }
    if (!response.ok) {
      throw new Error(`${path} failed (${response.status}): ${json.error || json.message || text}`);
    }

    return json;
  }

  authHeaders() {
    if (this.userAuthToken) return { Authorization: `Bearer ${this.userAuthToken}` };
    return { 'x-addc-companion-token': this.companionSessionToken };
  }
}

function parseJsonResponse(text) {
  if (!text) return {};
  try {
    return JSON.parse(text);
  } catch {
    return { message: text };
  }
}

function shouldIgnoreStartingScoreSnapshot(liveMatch, normalized) {
  if (normalized.normalizedEventType === 'match_completed') return false;
  if (!isStartingScoreSnapshot(normalized.scoreChange)) return false;
  if (isExplicitReset(normalized)) return false;
  const sameExternalMatch = !normalized.externalMatchId || !liveMatch.autodarts_match_id || normalized.externalMatchId === liveMatch.autodarts_match_id;
  const liveScoreProgressed = Number.isInteger(liveMatch.home_score) && Number.isInteger(liveMatch.away_score)
    && (liveMatch.home_score !== 501 || liveMatch.away_score !== 501);
  return sameExternalMatch && liveScoreProgressed;
}

function isStartingScoreSnapshot(scoreChange = {}) {
  return scoreChange.home_score === 501 && scoreChange.away_score === 501;
}

function isExplicitReset(normalized) {
  return normalized.scoreChange?.adapter_event_kind === 'manual_reset'
    || normalized.scoreChange?.source === 'manual_test_reset'
    || normalized.rawPayload?.source === 'manual_test_reset'
    || normalized.rawPayload?.data?.source === 'manual_test_reset';
}

function withoutRemainingScores(scoreChange = {}) {
  const copy = { ...scoreChange };
  delete copy.home_score;
  delete copy.away_score;
  return copy;
}
