import {
  reservationTaskAudits,
  reservationTaskDetails,
} from '@/fixtures/reservation'
import { getReservationHotelId } from '@/config/reservationConfig'
import { getJson, sendJson } from '@/services/httpClient'
import type {
  ReservationManualReviewConversionRequest,
  ReservationManualReviewConversionResult,
  ReservationManualReviewResolutionRequest,
  ReservationManualReviewResolutionResult,
  ReservationOperaOperationResult,
  ReservationOperaSimulationRequest,
  ReservationOrderDetailResult,
  ReservationOrderListFilters,
  ReservationOrderListResult,
  ReservationRecord,
  ReservationTaskAuditListResult,
  ReservationTaskDetailResult,
  ReservationTaskListFilters,
  ReservationTaskListResult,
  ReservationTaskPayloadMutationRequest,
  ReservationTaskPayloadMutationResult,
  SourceMessageConversationResult,
} from '@/types/reservation'

const useReservationFixtures = import.meta.env.VITE_RESERVATION_USE_FIXTURES === 'true'

export async function fetchReservationOrders(
  filters: ReservationOrderListFilters = {},
): Promise<ReservationOrderListResult> {
  return getJson<ReservationOrderListResult>(withQuery('/api/reservation/orders', withReservationHotel(filters)))
}

export async function fetchReservationTaskList(
  filters: ReservationTaskListFilters = {},
): Promise<ReservationTaskListResult> {
  return getJson<ReservationTaskListResult>(withQuery('/api/reservation/tasks', withReservationHotel(filters)))
}

export async function fetchReservationOrderDetail(orderId: string): Promise<ReservationOrderDetailResult> {
  return getJson<ReservationOrderDetailResult>(
    withQuery(`/api/reservation/orders/${encodeURIComponent(orderId)}`, {
      hotel_id: getReservationHotelId(),
      include_tasks: true,
      include_source_summary: true,
    }),
  )
}

export async function fetchSourceMessageConversation(
  sourceMessageId: string,
): Promise<SourceMessageConversationResult> {
  return getJson<SourceMessageConversationResult>(
    `/api/source-messages/${encodeURIComponent(sourceMessageId)}/conversation`,
  )
}

export async function fetchReservationTaskDetail(taskId: string): Promise<ReservationTaskDetailResult> {
  if (useReservationFixtures) {
    return fixtureTaskDetail(taskId)
  }
  return getJson<ReservationTaskDetailResult>(`/api/reservation/tasks/${taskId}`)
}

export async function saveReservationTaskDraft(
  taskId: string,
  request: ReservationTaskPayloadMutationRequest,
): Promise<ReservationTaskPayloadMutationResult> {
  if (useReservationFixtures) {
    return fixtureTaskMutation(taskId, request, false)
  }
  return sendJson<ReservationTaskPayloadMutationResult>(`/api/reservation/tasks/${taskId}/draft`, 'PUT', request)
}

export async function confirmReservationTask(
  taskId: string,
  request: ReservationTaskPayloadMutationRequest,
): Promise<ReservationTaskPayloadMutationResult> {
  if (useReservationFixtures) {
    return fixtureTaskMutation(taskId, request, true)
  }
  return sendJson<ReservationTaskPayloadMutationResult>(`/api/reservation/tasks/${taskId}/confirm`, 'POST', request)
}

export async function convertReservationManualReviewTask(
  taskId: string,
  request: ReservationManualReviewConversionRequest,
): Promise<ReservationManualReviewConversionResult> {
  return sendJson<ReservationManualReviewConversionResult>(
    `/api/reservation/tasks/${encodeURIComponent(taskId)}/manual-review-conversions`,
    'POST',
    request,
  )
}

export async function resolveReservationManualReviewTask(
  taskId: string,
  request: ReservationManualReviewResolutionRequest,
): Promise<ReservationManualReviewResolutionResult> {
  if (useReservationFixtures) {
    return fixtureManualReviewResolution(taskId, request)
  }
  return sendJson<ReservationManualReviewResolutionResult>(
    `/api/reservation/tasks/${encodeURIComponent(taskId)}/manual-review-resolutions`,
    'POST',
    request,
  )
}

export async function executeReservationOperaOperation(
  taskId: string,
  operationId: string,
  request: ReservationOperaSimulationRequest,
): Promise<ReservationOperaOperationResult> {
  if (useReservationFixtures) {
    return fixtureOperaOperation(taskId, operationId, request, 'execute')
  }
  return sendJson<ReservationOperaOperationResult>(
    `/api/reservation/tasks/${taskId}/opera-operations/${operationId}/execute`,
    'POST',
    request,
  )
}

export async function retryReservationOperaOperation(
  taskId: string,
  operationId: string,
  request: ReservationOperaSimulationRequest,
): Promise<ReservationOperaOperationResult> {
  if (useReservationFixtures) {
    return fixtureOperaOperation(taskId, operationId, request, 'retry')
  }
  return sendJson<ReservationOperaOperationResult>(
    `/api/reservation/tasks/${taskId}/opera-operations/${operationId}/retry`,
    'POST',
    request,
  )
}

export async function fetchReservationTaskAudits(taskId: string): Promise<ReservationTaskAuditListResult> {
  if (useReservationFixtures) {
    return Promise.resolve(
      reservationTaskAudits.find((audit) => audit.task_id === taskId) ?? {
        task_id: taskId,
        items: [],
      },
    )
  }
  return getJson<ReservationTaskAuditListResult>(`/api/reservation/tasks/${taskId}/audits`)
}

function withQuery(path: string, params: object): string {
  const searchParams = new URLSearchParams()
  Object.entries(params).forEach(([key, value]) => {
    if (
      value === undefined ||
      value === null ||
      value === '' ||
      !['string', 'number', 'boolean'].includes(typeof value)
    ) {
      return
    }
    searchParams.set(key, String(value))
  })
  const query = searchParams.toString()
  return query ? `${path}?${query}` : path
}

function withReservationHotel<T extends { hotel_id?: string }>(filters: T): T {
  return {
    hotel_id: getReservationHotelId() ?? undefined,
    ...filters,
  }
}

function fixtureTaskDetail(taskId: string): Promise<ReservationTaskDetailResult> {
  const detail = reservationTaskDetails.find((item) => item.task_id === taskId)
  if (!detail) {
    throw new Error(`Reservation task ${taskId} was not found in frontend fixture data.`)
  }
  return Promise.resolve(structuredClone(detail))
}

function fixtureTaskMutation(
  taskId: string,
  request: ReservationTaskPayloadMutationRequest,
  confirmed: boolean,
): Promise<ReservationTaskPayloadMutationResult> {
  return fixtureTaskDetail(taskId).then((detail) => ({
    task_id: detail.task_id,
    order_id: detail.order_id,
    task_status: confirmed ? 'READY' : detail.task_status,
    draft_payload: request.field_values,
    confirmed_payload: confirmed ? request.field_values : detail.confirmed_payload,
    opera_operations: confirmed ? detail.opera_operations : [],
  }))
}

function fixtureManualReviewResolution(
  taskId: string,
  request: ReservationManualReviewResolutionRequest,
): Promise<ReservationManualReviewResolutionResult> {
  return fixtureTaskDetail(taskId).then((detail) => {
    const fieldValues = Object.fromEntries(
      request.field_overrides.map((item) => [fieldOverrideToFixturePath(item), item.value]),
    )
    return {
      task_id: detail.task_id,
      order_id: detail.order_id,
      task_status: 'READY',
      review_status: 'RESOLVED',
      review_resolution: {
        confirmed_order_id: request.confirmed_order_id ?? detail.order_id,
        reason: request.reason ?? null,
        field_overrides: request.field_overrides,
      },
      confirmed_payload: {
        field_values: fieldValues,
        legacy_field_values: legacyFieldValues(fieldValues),
        effective_payload: effectivePayload(fieldValues),
      },
      opera_operations: detail.opera_operations,
    }
  })
}

function fieldOverrideToFixturePath(
  fieldOverride: ReservationManualReviewResolutionRequest['field_overrides'][number],
): string {
  if (fieldOverride.field_path) {
    return canonicalFixtureFieldPath(fieldOverride.field_path)
  }
  if (fieldOverride.field_pointer) {
    return canonicalFixtureFieldPath(fieldOverride.field_pointer.replace(/^\//, '').replace(/\//g, '.'))
  }
  throw new Error('Manual review field override requires field_pointer or field_path')
}

function canonicalFixtureFieldPath(fieldPath: string): string {
  if (fieldPath === 'extracted_fields.room_quantity') {
    return 'extracted_fields.room_items.0.room_quantity'
  }
  if (fieldPath === 'extracted_fields.room_type') {
    return 'extracted_fields.room_items.0.room_type_raw'
  }
  if (fieldPath === 'extracted_fields.pms_room_type_code') {
    return 'extracted_fields.room_items.0.pms_room_type_code'
  }
  return fieldPath
}

function legacyFieldValues(fieldValues: Record<string, unknown>): Record<string, unknown> {
  return Object.entries(fieldValues).reduce<Record<string, unknown>>((legacyValues, [fieldPath, value]) => {
    if (fieldPath === 'extracted_fields.room_items.0.room_quantity') {
      legacyValues['extracted_fields.room_quantity'] = value
    }
    if (fieldPath === 'extracted_fields.room_items.0.room_type_raw') {
      legacyValues['extracted_fields.room_type'] = value
    }
    if (fieldPath === 'extracted_fields.room_items.0.pms_room_type_code') {
      legacyValues['extracted_fields.pms_room_type_code'] = value
    }
    return legacyValues
  }, {})
}

function effectivePayload(fieldValues: Record<string, unknown>): ReservationRecord {
  return Object.entries(fieldValues).reduce<ReservationRecord>((payload, [fieldPath, value]) => {
    writeNestedFieldValue(payload, fieldPath, value)
    return payload
  }, {})
}

function writeNestedFieldValue(payload: ReservationRecord, fieldPath: string, value: unknown): void {
  const segments = fieldPath.split('.')
  let current: ReservationRecord | unknown[] = payload
  segments.forEach((segment, index) => {
    const last = index === segments.length - 1
    const nextSegment = segments[index + 1]
    if (last) {
      if (Array.isArray(current)) {
        current[Number(segment)] = value
      } else {
        current[segment] = value
      }
      return
    }
    const nextIsArray = nextSegment !== undefined && /^\d+$/.test(nextSegment)
    if (Array.isArray(current)) {
      const arrayIndex = Number(segment)
      current[arrayIndex] ??= nextIsArray ? [] : {}
      current = current[arrayIndex] as ReservationRecord | unknown[]
      return
    }
    current[segment] ??= nextIsArray ? [] : {}
    current = current[segment] as ReservationRecord | unknown[]
  })
}

async function fixtureOperaOperation(
  taskId: string,
  operationId: string,
  request: ReservationOperaSimulationRequest,
  action: 'execute' | 'retry',
): Promise<ReservationOperaOperationResult> {
  const detail = await fixtureTaskDetail(taskId)
  const operation = detail.opera_operations.find((item) => item.operation_id === operationId)
  if (!operation) {
    throw new Error(`Reservation OPERA operation ${operationId} was not found in frontend fixture data.`)
  }

  const success = request.simulate_success !== false
  const attemptNumber = operation.attempt_count + 1
  return {
    ...operation,
    operation_status: success ? 'SUCCEEDED' : 'FAILED',
    attempt_count: attemptNumber,
    last_error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure',
    attempts: [
      ...operation.attempts,
      {
        attempt_id: `${operationId}-${action}-${attemptNumber}`,
        attempt_number: attemptNumber,
        attempt_status: success ? 'SUCCEEDED' : 'FAILED',
        response_payload: success ? { simulated: true } : null,
        error_message: success ? null : request.failure_message ?? 'Frontend simulated OPERA failure',
        started_at: new Date().toISOString(),
        finished_at: new Date().toISOString(),
      },
    ],
  }
}
