// FILE: browser-panel.logic.ts
// Purpose: Holds the address-bar sync rules and suggestions for the in-app browser panel.
// Layer: Component logic helper
// Exports: browserAddressDisplayValue, normalizeBrowserAddressInput, buildBrowserAddressSuggestions
// Depends on: browser tab metadata and owner-local browser history

import type { BrowserHistoryEntry, BrowserTabState } from '~/store/browser-panel'

const ABOUT_BLANK_URL = 'about:blank'
const BROWSER_SUGGESTION_LIMIT = 6
const SEARCH_URL_PREFIX = 'https://www.google.com/search?q='

interface ResolveBrowserAddressSyncInput {
  activeTabId: string | null
  previousActiveTabId: string | null
  savedDraft: string | undefined
  nextDisplayValue: string
  lastSyncedValue: string | undefined
  isEditing: boolean
}

type BrowserAddressSyncDecision
  = | {
      type: 'keep'
    }
    | {
      type: 'replace'
      value: string
      syncedValue: string | undefined
    }

export interface BrowserAddressSuggestion {
  id: string
  kind: 'navigate' | 'tab' | 'history'
  title: string
  detail: string
  url: string
  tabId?: string
  faviconUrl?: string | null
}

interface BuildBrowserAddressSuggestionsInput {
  query: string
  activeTabId: string | null
  tabs: Array<Pick<BrowserTabState, 'id' | 'title' | 'url' | 'faviconUrl' | 'lastCommittedUrl'>>
  recentHistory: BrowserHistoryEntry[]
}

export interface BrowserChromeStatus {
  tone: 'default' | 'error'
  label: string
}

// Hides about:blank from the address bar so new tabs behave like real browsers.
export function browserAddressDisplayValue(
  tab: Pick<BrowserTabState, 'url'> | null | undefined,
): string {
  const nextUrl = tab?.url?.trim() ?? ''
  return nextUrl === ABOUT_BLANK_URL ? '' : nextUrl
}

function looksLikeUrlInput(value: string): boolean {
  return (
    value.includes('.')
    || value.startsWith('localhost')
    || value.startsWith('127.0.0.1')
    || value.startsWith('0.0.0.0')
    || value.startsWith('[::1]')
  )
}

// Normalizes typed text into the url or search target we should submit.
export function normalizeBrowserAddressInput(input: string): string {
  const trimmed = input.trim()
  if (trimmed.length === 0) {
    return ABOUT_BLANK_URL
  }

  try {
    const withScheme = new URL(trimmed)
    if (withScheme.protocol === 'http:' || withScheme.protocol === 'https:') {
      return withScheme.toString()
    }
    if (withScheme.protocol === 'about:') {
      return withScheme.toString()
    }
  }
 catch {
    // Fall through to browser-style heuristics below.
  }

  if (trimmed.includes(' ')) {
    return `${SEARCH_URL_PREFIX}${encodeURIComponent(trimmed)}`
  }

  if (looksLikeUrlInput(trimmed)) {
    const prefersHttp
      = trimmed.startsWith('localhost')
        || trimmed.startsWith('127.0.0.1')
        || trimmed.startsWith('0.0.0.0')
        || trimmed.startsWith('[::1]')
    const scheme = prefersHttp ? 'http' : 'https'
    try {
      return new URL(`${scheme}://${trimmed}`).toString()
    }
 catch {
      return `${SEARCH_URL_PREFIX}${encodeURIComponent(trimmed)}`
    }
  }

  return `${SEARCH_URL_PREFIX}${encodeURIComponent(trimmed)}`
}

function normalizeQuery(value: string): string {
  return value.trim().toLowerCase()
}

function displaySuggestionUrl(value: string): string {
  return value.trim().replace(/^about:blank$/i, '')
}

function suggestionMatches(query: string, candidate: string): boolean {
  if (query.length === 0) {
    return true
  }
  return normalizeQuery(candidate).includes(query)
}

function pushSuggestion(
  suggestions: BrowserAddressSuggestion[],
  seenUrls: Set<string>,
  suggestion: BrowserAddressSuggestion,
): void {
  if (suggestions.length >= BROWSER_SUGGESTION_LIMIT || seenUrls.has(suggestion.url)) {
    return
  }

  seenUrls.add(suggestion.url)
  suggestions.push(suggestion)
}

// Builds browser-like suggestions from the typed query, open tabs, and recent history.
export function buildBrowserAddressSuggestions(
  input: BuildBrowserAddressSuggestionsInput,
): BrowserAddressSuggestion[] {
  const query = normalizeQuery(input.query)
  const suggestions: BrowserAddressSuggestion[] = []
  const seenUrls = new Set<string>()
  const directTarget = normalizeBrowserAddressInput(input.query)

  if (query.length > 0) {
    const directTitle = directTarget.startsWith(SEARCH_URL_PREFIX)
      ? `Search the web for "${input.query.trim()}"`
      : `Open ${directTarget}`
    pushSuggestion(suggestions, seenUrls, {
      id: `direct:${directTarget}`,
      kind: 'navigate',
      title: directTitle,
      detail: directTarget,
      url: directTarget,
    })
  }

  for (const tab of input.tabs) {
    const tabUrl = displaySuggestionUrl(tab.lastCommittedUrl ?? tab.url)
    if (tabUrl.length === 0 || tab.id === input.activeTabId) {
      continue
    }
    if (!suggestionMatches(query, `${tab.title} ${tabUrl}`)) {
      continue
    }
    pushSuggestion(suggestions, seenUrls, {
      id: `tab:${tab.id}`,
      kind: 'tab',
      title: tab.title || tabUrl,
      detail: tabUrl,
      url: tabUrl,
      tabId: tab.id,
      faviconUrl: tab.faviconUrl,
    })
  }

  for (const entry of input.recentHistory) {
    const entryUrl = displaySuggestionUrl(entry.url)
    if (entryUrl.length === 0) {
      continue
    }
    if (!suggestionMatches(query, `${entry.title} ${entryUrl}`)) {
      continue
    }
    pushSuggestion(suggestions, seenUrls, {
      id: `history:${entry.url}`,
      kind: 'history',
      title: entry.title || entryUrl,
      detail: entryUrl,
      url: entryUrl,
    })
  }

  return suggestions.slice(0, BROWSER_SUGGESTION_LIMIT)
}

// Only shows transient browser state; the address field already reflects the active URL.
export function resolveBrowserChromeStatus(input: {
  localError: string | null
  threadLastError: string | null | undefined
  activeTabStatus: string
  hasActiveTab: boolean
  workspaceReady: boolean
}): BrowserChromeStatus | null {
  if (input.localError) {
    return {
      tone: 'error',
      label: input.localError,
    }
  }

  if (input.threadLastError) {
    return {
      tone: 'error',
      label: input.threadLastError,
    }
  }

  if (!input.hasActiveTab) {
    return {
      tone: 'default',
      label: input.workspaceReady ? 'No tabs open' : 'Starting browser...',
    }
  }

  if (input.activeTabStatus === 'suspended') {
    return {
      tone: 'default',
      label: 'Restoring tab...',
    }
  }

  return null
}

// Decides when browser state should replace the visible address input.
export function resolveBrowserAddressSync(
  input: ResolveBrowserAddressSyncInput,
): BrowserAddressSyncDecision {
  if (!input.activeTabId) {
    return {
      type: 'replace',
      value: '',
      syncedValue: undefined,
    }
  }

  if (input.activeTabId !== input.previousActiveTabId) {
    if (input.savedDraft !== undefined) {
      return {
        type: 'replace',
        value: input.savedDraft,
        syncedValue: input.lastSyncedValue,
      }
    }

    return {
      type: 'replace',
      value: input.nextDisplayValue,
      syncedValue: input.nextDisplayValue,
    }
  }

  if (input.isEditing || input.lastSyncedValue === input.nextDisplayValue) {
    return { type: 'keep' }
  }

  return {
    type: 'replace',
    value: input.nextDisplayValue,
    syncedValue: input.nextDisplayValue,
  }
}
