import type { PoolConnection, ResultSetHeader, RowDataPacket } from 'mysql2/promise';

import { execute, getConnection, query } from './pool.js';
import type { EventChoice, EventInstanceStatus } from '../events/v2/types.js';

interface EventInstanceRow extends RowDataPacket {
  instance_id: string;
  player_id: string;
  event_id: string;
  status: EventInstanceStatus;
  prompt: string;
  choices_json: string;
  context_json: string | null;
  selected_choice_id: string | null;
  created_at: Date;
  answered_at: Date | null;
  resolved_at: Date | null;
}

export interface EventInstanceRecord {
  instanceId: string;
  playerId: string;
  eventId: string;
  status: EventInstanceStatus;
  prompt: string;
  choices: EventChoice[];
  context: Record<string, unknown> | null;
  selectedChoiceId: string | null;
  createdAt: string;
  answeredAt: string | null;
  resolvedAt: string | null;
}

export interface CreateEventInstanceInput {
  instanceId: string;
  playerId: string;
  eventId: string;
  prompt: string;
  choices: EventChoice[];
  context?: Record<string, unknown>;
}

export interface ResolveEventInstanceInput {
  resolutionText: string;
  effects?: Record<string, unknown>;
}

function parseChoices(jsonText: string): EventChoice[] {
  try {
    const parsed = JSON.parse(jsonText) as unknown;
    if (Array.isArray(parsed)) {
      return parsed as EventChoice[];
    }
  } catch {
    // no-op fallback
  }
  return [];
}

function parseContext(jsonText: string | null): Record<string, unknown> | null {
  if (!jsonText) {
    return null;
  }

  try {
    const parsed = JSON.parse(jsonText) as unknown;
    if (parsed && typeof parsed === 'object') {
      return parsed as Record<string, unknown>;
    }
  } catch {
    // no-op fallback
  }

  return null;
}

function toRecord(row: EventInstanceRow): EventInstanceRecord {
  return {
    instanceId: row.instance_id,
    playerId: row.player_id,
    eventId: row.event_id,
    status: row.status,
    prompt: row.prompt,
    choices: parseChoices(row.choices_json),
    context: parseContext(row.context_json),
    selectedChoiceId: row.selected_choice_id,
    createdAt: row.created_at.toISOString(),
    answeredAt: row.answered_at ? row.answered_at.toISOString() : null,
    resolvedAt: row.resolved_at ? row.resolved_at.toISOString() : null,
  };
}

export async function createEventInstance(
  input: CreateEventInstanceInput
): Promise<EventInstanceRecord> {
  await execute(
    `INSERT INTO event_instances
      (instance_id, player_id, event_id, status, prompt, choices_json, context_json, created_at, updated_at)
     VALUES (?, ?, ?, 'pending', ?, ?, ?, NOW(), NOW())`,
    [
      input.instanceId,
      input.playerId,
      input.eventId,
      input.prompt,
      JSON.stringify(input.choices),
      input.context ? JSON.stringify(input.context) : null,
    ]
  );

  return {
    instanceId: input.instanceId,
    playerId: input.playerId,
    eventId: input.eventId,
    status: 'pending',
    prompt: input.prompt,
    choices: input.choices,
    context: input.context ?? null,
    selectedChoiceId: null,
    createdAt: new Date().toISOString(),
    answeredAt: null,
    resolvedAt: null,
  };
}

export async function getPendingEventInstances(playerId: string): Promise<EventInstanceRecord[]> {
  const rows = await query<EventInstanceRow[]>(
    `SELECT
      instance_id,
      player_id,
      event_id,
      status,
      prompt,
      choices_json,
      context_json,
      selected_choice_id,
      created_at,
      answered_at,
      resolved_at
     FROM event_instances
     WHERE player_id = ? AND status = 'pending'
     ORDER BY created_at ASC`,
    [playerId]
  );

  return rows.map(toRecord);
}

export async function answerEventInstance(instanceId: string, choiceId: string): Promise<boolean> {
  const result = await execute(
    `UPDATE event_instances
     SET status = 'answered', selected_choice_id = ?, answered_at = NOW(), updated_at = NOW()
     WHERE instance_id = ? AND status = 'pending'`,
    [choiceId, instanceId]
  );

  return (result as ResultSetHeader).affectedRows > 0;
}

export async function resolveEventInstance(
  instanceId: string,
  resolution: ResolveEventInstanceInput
): Promise<boolean> {
  const result = await execute(
    `UPDATE event_instances
     SET status = 'resolved', context_json = ?, resolved_at = NOW(), updated_at = NOW()
     WHERE instance_id = ? AND status IN ('pending', 'answered')`,
    [JSON.stringify(resolution), instanceId]
  );

  return (result as ResultSetHeader).affectedRows > 0;
}

export async function ensureEventInstancesTable(existingConnection?: PoolConnection): Promise<void> {
  let connection = existingConnection;
  let releaseOnExit = false;

  if (!connection) {
    connection = await getConnection();
    releaseOnExit = true;
  }

  try {
    await connection.execute(`
      CREATE TABLE IF NOT EXISTS event_instances (
        instance_id VARCHAR(255) PRIMARY KEY,
        player_id VARCHAR(255) NOT NULL,
        event_id VARCHAR(255) NOT NULL,
        status ENUM('pending', 'answered', 'resolved', 'cancelled') NOT NULL DEFAULT 'pending',
        prompt TEXT NOT NULL,
        choices_json LONGTEXT NOT NULL,
        context_json LONGTEXT NULL,
        selected_choice_id VARCHAR(255) NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        answered_at DATETIME NULL,
        resolved_at DATETIME NULL,
        INDEX idx_event_instances_player_status (player_id, status),
        INDEX idx_event_instances_event (event_id)
      )
    `);
  } finally {
    if (releaseOnExit) {
      connection.release();
    }
  }
}
