import type { Player } from '../../models/Player.js';
import {
  sendLiveActivityUpdate,
  type CharacterLiveActivityContentState,
  type SendResult,
} from './pushNotificationService.js';

export const LIVE_ACTIVITY_REMOTE_UPDATE_INTERVAL_MS = 30_000;

interface UpdateOptions {
  major?: boolean;
}

export interface LiveActivityUpdateResult {
  sent: boolean;
  reason?: string;
  sendResult?: SendResult;
}

export function createLiveActivityContentState(
  player: Player,
  now: Date = new Date()
): CharacterLiveActivityContentState {
  const character = player.c;

  return {
    gameDate: player.date || '',
    gameTime: formatGameTime(player.hourOfDay, player.minuteOfHour),
    age: character.ageYears ?? 0,
    season: player.season || '',
    location: character.location || 'Life in progress',
    statusMessage: character.intraDayMessage || statusLabel(player.status),
    energy: clampInt(character.calcEnergy ?? character.energy ?? 0, 0, 999),
    health: clampInt(character.health ?? 0, 0, 100),
    happiness: clampInt(character.happiness ?? 0, 0, 100),
    speedLabel: speedLabel(player.gameSpeed),
    isRunning: player.controller === 'active' && player.status === 'playing' && character.status !== 'dead',
    updatedAt: now.toISOString(),
  };
}

export function shouldSendLiveActivityUpdate(
  player: Player,
  now: Date = new Date(),
  options: UpdateOptions = {}
): boolean {
  const registration = player.liveActivity;
  if (!registration?.pushToken || !registration.activityId) {
    return false;
  }

  if (options.major) {
    return true;
  }

  if (!registration.lastSentAt) {
    return true;
  }

  const lastSentMs = Date.parse(registration.lastSentAt);
  if (Number.isNaN(lastSentMs)) {
    return true;
  }

  return now.getTime() - lastSentMs >= LIVE_ACTIVITY_REMOTE_UPDATE_INTERVAL_MS;
}

export async function queueLiveActivityUpdate(
  player: Player,
  options: UpdateOptions = {},
  now: Date = new Date()
): Promise<LiveActivityUpdateResult> {
  const registration = player.liveActivity;
  if (!registration?.pushToken || !registration.activityId) {
    return { sent: false, reason: 'no_live_activity' };
  }

  if (!shouldSendLiveActivityUpdate(player, now, options)) {
    return { sent: false, reason: 'throttled' };
  }

  const sendResult = await sendLiveActivityUpdate(registration.pushToken, {
    event: 'update',
    timestamp: Math.floor(now.getTime() / 1000),
    contentState: createLiveActivityContentState(player, now),
  });

  if (sendResult.success) {
    registration.lastSentAt = now.toISOString();
  }

  return { sent: sendResult.success, sendResult };
}

export async function endLiveActivityForPlayer(
  player: Player,
  now: Date = new Date()
): Promise<LiveActivityUpdateResult> {
  const registration = player.liveActivity;
  if (!registration?.pushToken) {
    player.liveActivity = undefined;
    return { sent: false, reason: 'no_live_activity' };
  }

  const sendResult = await sendLiveActivityUpdate(registration.pushToken, {
    event: 'end',
    timestamp: Math.floor(now.getTime() / 1000),
    dismissalDate: Math.floor((now.getTime() + 60_000) / 1000),
    contentState: {
      ...createLiveActivityContentState(player, now),
      isRunning: false,
    },
  });

  player.liveActivity = undefined;

  return { sent: sendResult.success, sendResult };
}

function formatGameTime(hour: number, minute: number): string {
  const safeHour = Number.isFinite(hour) ? hour : 0;
  const safeMinute = Number.isFinite(minute) ? minute : 0;
  const period = safeHour >= 12 ? 'PM' : 'AM';
  const displayHour = safeHour % 12 === 0 ? 12 : safeHour % 12;
  return `${displayHour}:${String(safeMinute).padStart(2, '0')} ${period}`;
}

function speedLabel(speed: number): string {
  switch (speed) {
    case 10000:
      return 'Slowest';
    case 1000:
      return 'Slow';
    case 500:
      return 'Normal';
    case 50:
      return 'Fast';
    case 20:
      return 'Fastest';
    case 1:
      return 'Instant';
    default:
      return String(speed);
  }
}

function statusLabel(status: string): string {
  if (status === 'playing') return 'Living life';
  if (status === 'creating') return 'Creating character';
  return status || 'Life in progress';
}

function clampInt(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, Math.round(value)));
}
