/**
 * Event Response Handlers
 *
 * Handles responses to questionEvents - when a player answers a question,
 * this processes the answer and updates the game state.
 *
 * Also handles responses to pending conversation events from AI tool calls
 * (activity invites, date requests, emotional moments, etc.)
 */

import type { PlayerSession } from '../game/PlayerSession.js';
import { config } from '../config.js';
import { buildEventError } from '../events/v2/protocol.js';
import { getEventRuntime } from '../events/v2/runtime.js';
import { parseEventResponseCommand } from '../events/v2/schema.js';
import { processEventResponse } from '../events/conversations/index.js';

interface QuestionEventPayload {
  id?: string;
  type?: string;
  response?: string | { option?: string; index?: number; choiceId?: string };
  message?: {
    option?: string;
    choiceId?: string;
    index?: number;
    energyCost?: number;
    moneyCost?: number;
    diamondCost?: number;
  };
  // Callback data for pending conversation events from AI tool calls
  // Additional event-specific fields are spread directly (e.g., activityType, costs)
  _callbackData?: {
    eventType: 'activity_invite' | 'date_request' | 'emotional_moment' | 'favor_request' | 'gift_received' | 'confession';
    characterId: string;
    eventId: string;
    [key: string]: unknown;  // Event-specific data (activityType, costs, etc.)
  };
}

/**
 * Handle response to a pending conversation event from AI tool calls
 * (activity invites, date requests, emotional moments, etc.)
 */
async function handlePendingConversationEventResponse(
  data: QuestionEventPayload,
  session: PlayerSession
): Promise<void> {
  const player = session.player;
  const callbackData = data._callbackData!;

  console.log(`Processing pending conversation event response: ${callbackData.eventType} from ${callbackData.characterId}`);

  // Determine which option was selected
  // Response can be in different formats - prefer explicit index/choiceId when present.
  let selectedOption = 0;
  const choiceLike =
    (typeof data.response === 'object' && data.response?.choiceId) ||
    data.message?.choiceId;
  const indexLike =
    (typeof data.response === 'object' && typeof data.response?.index === 'number'
      ? data.response.index
      : undefined) ??
    (typeof data.message?.index === 'number' ? data.message.index : undefined);

  if (typeof indexLike === 'number' && Number.isFinite(indexLike)) {
    selectedOption = Math.max(0, Math.floor(indexLike));
  } else if (choiceLike && /^choice[_-]?\d+$/i.test(choiceLike)) {
    const parsedChoiceIndex = Number.parseInt(choiceLike.replace(/[^\d]/g, ''), 10);
    if (Number.isFinite(parsedChoiceIndex)) {
      selectedOption = Math.max(0, parsedChoiceIndex);
    }
  } else if (typeof data.response === 'string' && /^\d+$/.test(data.response.trim())) {
    selectedOption = Math.max(0, Number.parseInt(data.response.trim(), 10));
  }

  // Process the event response - pass the full callbackData object
  const result = processEventResponse(
    player,
    callbackData.eventId,
    selectedOption,
    callbackData as NonNullable<Parameters<typeof processEventResponse>[3]>
  );

  if (result.success) {
    console.log(`Pending event processed: ${result.message}`);

    // Note: Affinity and stat changes are already applied inside processEventResponse
    // The result.affinityChange and result.statChanges are for reporting only

    // Show feedback message to player if provided
    if (result.message) {
      session.send({
        type: 'messageEvent',
        id: `pending_response_${callbackData.eventId}`,
        message: result.message,
        date: player.date,
        hour: player.hourOfDay,
        title: selectedOption === 0 ? 'Accepted' : selectedOption === 1 ? 'Maybe Later' : 'Declined',
        image: '',
        energyCost: 0,
        diamondCost: 0,
        moneyCost: 0,
        affinityChange: result.affinityChange ?? 0,
        characters: callbackData.characterId ? [callbackData.characterId] : [],
      });
    }
  } else {
    console.warn(`Failed to process pending event: ${result.message}`);
  }

  // Reset game speed
  if (player.previousGameSpeed !== undefined) {
    player.gameSpeed = player.previousGameSpeed;
  } else {
    player.gameSpeed = config.SPEED_DEFAULT;
  }

  // Send updated player state
  session.sendPlayerObject();
}

/**
 * Handle questionEvent response (when player answers a question)
 * @deprecated Active event responses must use `eventResponse` with { eventId, choiceId }.
 * This handler is kept only for pending conversation callbacks that include `_callbackData`.
 */
export async function handleQuestionEvent(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  const data = payload as QuestionEventPayload;
  const player = session.player;

  // Check if this is a pending conversation event response (from AI tool calls)
  if (data._callbackData) {
    await handlePendingConversationEventResponse(data, session);
    return;
  }

  const candidate =
    payload && typeof payload === 'object' && 'message' in (payload as Record<string, unknown>)
      ? (payload as { message?: unknown }).message
      : payload;
  const hasEventResponseShape =
    !!candidate &&
    typeof candidate === 'object' &&
    'eventId' in (candidate as Record<string, unknown>) &&
    'choiceId' in (candidate as Record<string, unknown>);
  if (hasEventResponseShape) {
    await handleEventResponse(candidate, session);
    return;
  }

  console.warn('Rejected legacy questionEvent payload without _callbackData');
  session.send(
    buildEventError({
      code: 'UNSUPPORTED_LEGACY_QUESTION_EVENT',
      message: 'Use eventResponse with { eventId, choiceId } for event choices.',
    })
  );

  if (player.previousGameSpeed !== undefined) {
    player.gameSpeed = player.previousGameSpeed;
  } else {
    player.gameSpeed = config.SPEED_DEFAULT;
  }
  session.sendPlayerObject();
}

/**
 * Handle v2 event responses that use strict { eventId, choiceId } contracts.
 */
export async function handleEventResponse(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  try {
    const wrappedCommand =
      payload && typeof payload === 'object' && 'type' in payload
        ? payload
        : {
            type: 'eventResponse',
            message: payload,
          };

    const command = parseEventResponseCommand(wrappedCommand);
    const result = await getEventRuntime().respond(
      session.player as unknown as Parameters<ReturnType<typeof getEventRuntime>['respond']>[0],
      command.message
    );

    session.send(result);

    if (result.type === 'event_resolved') {
      if (session.player.previousGameSpeed !== undefined) {
        session.player.gameSpeed = session.player.previousGameSpeed;
      } else {
        session.player.gameSpeed = config.SPEED_DEFAULT;
      }

      session.player.askedQuestions.add(command.message.eventId);
      session.sendPlayerObject();
    }
  } catch (error) {
    console.error('Error processing eventResponse:', error);
    session.send(
      buildEventError({
        code: 'INVALID_EVENT_RESPONSE',
        message: 'Invalid event response payload',
      })
    );
  }
}

/**
 * Handle claimEvent (when player claims rewards from a message event)
 */
export async function handleClaimEvent(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  const data = payload as { eventId?: string; timestamp?: string };
  const eventId = data.eventId;

  if (!eventId) {
    console.warn('claimEvent missing eventId');
    return;
  }

  console.log(`Player claiming event: ${eventId}`);

  // Mark event as claimed
  session.player.events.add(eventId);

  session.send({
    type: 'eventClaimed',
    eventId,
    success: true,
  });
}

interface GenericEventPayload {
  type?: string;
}

/**
 * Handle generic/fallback events.
 * @deprecated Legacy event-id dispatch is removed from active runtime.
 */
export async function handleGenericEvent(
  payload: unknown,
  session: PlayerSession
): Promise<void> {
  const data = payload as GenericEventPayload;
  const commandType = data.type ?? 'unknown';

  console.warn(`Rejected legacy generic event command: ${commandType}`);
  session.send(
    buildEventError({
      code: 'UNSUPPORTED_LEGACY_COMMAND',
      message: `Unsupported legacy command "${commandType}". Use explicit command handlers.`,
    })
  );
}
