import { logoutUser } from '@/stores/auth'

const hasLocalStorage =
  typeof localStorage !== 'undefined' && typeof localStorage.getItem === 'function'
const CROSS_TAB_REFRESH_KEY = 'lastRefreshAt'
const CROSS_TAB_REFRESH_MIN_INTERVAL = 8_000 // 8s — prevents concurrent multi-tab refresh

let unauthorizedInterceptorInstalled = false
let unauthorizedRedirectHandler: (() => void) | null = null
let unauthorizedHandlingInProgress = false

// Mutex for refresh token requests — prevents concurrent refresh calls
let refreshPromise: Promise<boolean> | null = null

export function setUnauthorizedRedirectHandlerForTests(handler: (() => void) | null): void {
  unauthorizedRedirectHandler = handler
}

/** Reset interceptor state (for tests). */
export function resetInterceptorStateForTests(): void {
  unauthorizedHandlingInProgress = false
  refreshPromise = null
  if (hasLocalStorage && typeof localStorage.removeItem === 'function') {
    localStorage.removeItem(CROSS_TAB_REFRESH_KEY)
  }
}

function handleUnauthorizedResponse(): void {
  if (unauthorizedHandlingInProgress) return
  unauthorizedHandlingInProgress = true
  logoutUser()
  if (typeof window === 'undefined') return
  if (window.location.pathname.startsWith('/auth')) return
  if (window.location.pathname === '/') return
  if (unauthorizedRedirectHandler) {
    unauthorizedRedirectHandler()
    return
  }
  window.location.assign('/')
}

/**
 * Attempt to refresh the access token by calling the refresh endpoint.
 * Returns true if refresh succeeded, false otherwise.
 * Uses a mutex so concurrent 401s only trigger one refresh call.
 * Also uses a localStorage timestamp to coordinate across tabs, preventing
 * concurrent refreshes that would trigger false-positive theft detection.
 */
async function attemptTokenRefresh(originalFetch: typeof fetch): Promise<boolean> {
  // Cross-tab coordination: if another tab refreshed recently, the new cookie
  // is already set. Skip the refresh call and let the original request retry.
  if (hasLocalStorage && typeof localStorage.getItem === 'function') {
    const lastRefresh = localStorage.getItem(CROSS_TAB_REFRESH_KEY)
    if (lastRefresh && Date.now() - Number(lastRefresh) < CROSS_TAB_REFRESH_MIN_INTERVAL) {
      return true
    }
  }

  if (refreshPromise) return refreshPromise

  refreshPromise = (async () => {
    try {
      const res = await originalFetch('/api/auth/refresh', { method: 'POST' })
      if (res.ok && hasLocalStorage && typeof localStorage.setItem === 'function') {
        localStorage.setItem(CROSS_TAB_REFRESH_KEY, String(Date.now()))
      }
      return res.ok
    } catch {
      return false
    } finally {
      refreshPromise = null
    }
  })()

  return refreshPromise
}

/** URLs that should NOT trigger a refresh attempt on 401. */
function isAuthUrl(url: string): boolean {
  return url.includes('/api/auth/refresh') || url.includes('/api/auth/login')
}

export function installUnauthorizedFetchInterceptor(): void {
  if (unauthorizedInterceptorInstalled || typeof window === 'undefined') return
  unauthorizedInterceptorInstalled = true

  const originalFetch = globalThis.fetch.bind(globalThis)
  const wrappedFetch = (async (...args: Parameters<typeof fetch>) => {
    const response = await originalFetch(...args)

    if (response.status === 401) {
      // Determine the request URL
      const requestUrl = typeof args[0] === 'string' ? args[0] : (args[0] as Request).url

      // Don't attempt refresh for auth endpoints themselves
      if (isAuthUrl(requestUrl)) {
        handleUnauthorizedResponse()
        return response
      }

      // Attempt silent refresh
      const refreshed = await attemptTokenRefresh(originalFetch)
      if (refreshed) {
        // Retry the original request with the new access token cookie
        return originalFetch(...args)
      }

      // Refresh failed — log out
      handleUnauthorizedResponse()
    }

    return response
  }) as typeof fetch

  window.fetch = wrappedFetch as typeof window.fetch
  globalThis.fetch = wrappedFetch as typeof globalThis.fetch
}

export async function parseErrorResponse(res: Response): Promise<{ msg: string; code?: string }> {
  try {
    const data = await res.json()
    return { msg: data.error || data.message || 'Error', code: data.code }
  } catch {
    const text = await res.text()
    return { msg: text || 'Error' }
  }
}

export function isEmailValid(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}

export function isPasswordStrong(password: string): boolean {
  return /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]{8,}$/.test(password)
}

/**
 * Fetch tracking events for a child with optional filters.
 */
export async function getTrackingEventsForChild(params: {
  childId: string
  entityType?: 'task' | 'reward' | 'penalty' | 'chore' | 'kindness'
  action?:
    | 'activated'
    | 'requested'
    | 'redeemed'
    | 'cancelled'
    | 'confirmed'
    | 'approved'
    | 'rejected'
    | 'reset'
  limit?: number
  offset?: number
}): Promise<Response> {
  const query = new URLSearchParams()
  query.set('child_id', params.childId)
  if (params.entityType) query.set('entity_type', params.entityType)
  if (params.action) query.set('action', params.action)
  if (params.limit) query.set('limit', params.limit.toString())
  if (params.offset) query.set('offset', params.offset.toString())

  return fetch(`/api/admin/tracking?${query.toString()}`)
}

/**
 * Set or update a custom value for a task/reward for a specific child.
 */
export async function setChildOverride(
  childId: string,
  entityId: string,
  entityType: 'task' | 'reward' | 'routine',
  customValue: number,
): Promise<Response> {
  return fetch(`/api/child/${childId}/override`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      entity_id: entityId,
      entity_type: entityType,
      custom_value: customValue,
    }),
  })
}

export async function setChildRoutineOverride(
  childId: string,
  routineId: string,
  customValue: number,
): Promise<Response> {
  return setChildOverride(childId, routineId, 'routine', customValue)
}

/**
 * Get all overrides for a specific child.
 */
export async function getChildOverrides(childId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/overrides`)
}

/**
 * Delete an override (reset to default).
 */
export async function deleteChildOverride(childId: string, entityId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/override/${entityId}`, {
    method: 'DELETE',
  })
}

// ── Chore Schedule API ──────────────────────────────────────────────────────

/**
 * Get the schedule for a specific child + task pair.
 */
export async function getChoreSchedule(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/task/${taskId}/schedule`)
}

/**
 * Create or replace the schedule for a specific child + task pair.
 */
export async function setChoreSchedule(
  childId: string,
  taskId: string,
  schedule: object,
): Promise<Response> {
  return fetch(`/api/child/${childId}/task/${taskId}/schedule`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(schedule),
  })
}

/**
 * Delete the schedule for a specific child + task pair.
 */
export async function deleteChoreSchedule(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/task/${taskId}/schedule`, {
    method: 'DELETE',
  })
}

/**
 * Get the schedule for a specific child + routine pair.
 */
export async function getRoutineSchedule(childId: string, routineId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/routine/${routineId}/schedule`)
}

/**
 * Create or replace the schedule for a specific child + routine pair.
 */
export async function setRoutineSchedule(
  childId: string,
  routineId: string,
  schedule: object,
): Promise<Response> {
  return fetch(`/api/child/${childId}/routine/${routineId}/schedule`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(schedule),
  })
}

/**
 * Delete the schedule for a specific child + routine pair.
 */
export async function deleteRoutineSchedule(childId: string, routineId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/routine/${routineId}/schedule`, {
    method: 'DELETE',
  })
}

/**
 * Extend a timed-out chore for the remainder of today only.
 * `localDate` is the client's local ISO date (e.g. '2026-02-22').
 */
export async function extendChoreTime(
  childId: string,
  taskId: string,
  localDate: string,
): Promise<Response> {
  return fetch(`/api/child/${childId}/task/${taskId}/extend`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ date: localDate }),
  })
}

/**
 * Extend a timed-out routine for the remainder of today only.
 * `localDate` is the client's local ISO date (e.g. '2026-02-22').
 */
export async function extendRoutineTime(
  childId: string,
  routineId: string,
  localDate: string,
): Promise<Response> {
  return fetch(`/api/child/${childId}/routine/${routineId}/extend`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ date: localDate }),
  })
}

// ── Chore Confirmation API ──────────────────────────────────────────────────

/**
 * Child confirms they completed a chore.
 */
export async function confirmChore(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/confirm-chore`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ task_id: taskId }),
  })
}

/**
 * Child cancels a pending chore confirmation.
 */
export async function cancelConfirmChore(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/cancel-confirm-chore`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ task_id: taskId }),
  })
}

/**
 * Parent approves a pending chore confirmation (awards points).
 */
export async function approveChore(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/approve-chore`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ task_id: taskId }),
  })
}

/**
 * Parent rejects a pending chore confirmation (no points, resets to available).
 */
export async function rejectChore(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/reject-chore`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ task_id: taskId }),
  })
}

/**
 * Parent resets a completed chore (points kept, chore can be confirmed again).
 */
export async function resetChore(childId: string, taskId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/reset-chore`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ task_id: taskId }),
  })
}

/**
 * Fetch all pending confirmations (both chores and rewards) for the current user.
 */
export async function fetchPendingConfirmations(): Promise<Response> {
  return fetch('/api/pending-confirmations')
}

/**
 * Child confirms they completed a routine.
 */
export async function confirmRoutine(childId: string, routineId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/confirm-routine`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ routine_id: routineId }),
  })
}

/**
 * Child cancels a pending routine confirmation.
 */
export async function cancelRoutineConfirmation(
  childId: string,
  routineId: string,
): Promise<Response> {
  return fetch(`/api/child/${childId}/cancel-routine-confirmation`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ routine_id: routineId }),
  })
}

/**
 * Parent approves a pending routine confirmation (awards points).
 */
export async function approveRoutine(childId: string, confirmationId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/approve-routine/${confirmationId}`, {
    method: 'POST',
  })
}

/**
 * Parent rejects a pending routine confirmation (no points, resets to available).
 */
export async function rejectRoutine(childId: string, confirmationId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/reject-routine/${confirmationId}`, {
    method: 'POST',
  })
}

/**
 * Parent resets an approved routine (routine can be confirmed again).
 */
export async function resetRoutine(childId: string, confirmationId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/reset-routine/${confirmationId}`, {
    method: 'POST',
  })
}

/**
 * Parent triggers a routine directly (awards points immediately).
 */
export async function triggerRoutineAsParent(
  childId: string,
  routineId: string,
): Promise<Response> {
  return fetch(`/api/child/${childId}/trigger-routine`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ routine_id: routineId }),
  })
}

/**
 * Assign a single routine to a child.
 */
export async function assignRoutine(childId: string, routineId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/assign-routine`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ routine_id: routineId }),
  })
}

/**
 * Remove a single routine assignment from a child.
 */
export async function removeRoutine(childId: string, routineId: string): Promise<Response> {
  return fetch(`/api/child/${childId}/remove-routine`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ routine_id: routineId }),
  })
}

/**
 * Replace all routine assignments for a child.
 */
export async function setChildRoutines(childId: string, routineIds: string[]): Promise<Response> {
  return fetch(`/api/child/${childId}/set-routines`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ routine_ids: routineIds }),
  })
}
