import { invoke } from '@tauri-apps/api/core'
import dayjs from 'dayjs'

import { showNotice } from '@/services/notice-service'
import type { ProxyViewV1 } from '@/types/proxy-view'
import { debugLog } from '@/utils/debug'

export async function copyClashEnv() {
  return invoke<void>('copy_clash_env')
}

export async function getProfiles() {
  return invoke<IProfilesConfig>('get_profiles')
}

export async function enhanceProfiles() {
  return (
    (await invoke<ValidationOutcome>('enhance_profiles')).status === 'valid'
  )
}

export async function patchProfilesConfig(profiles: IProfilesConfig) {
  return invoke<ValidationOutcome>('patch_profiles_config', { profiles })
}

export async function createProfile(
  item: Partial<IProfileItem>,
  fileData?: string | null,
) {
  return invoke<void>('create_profile', { item, fileData })
}

export async function viewProfile(index: string) {
  return invoke<void>('view_profile', { index })
}

export async function readProfileFile(index: string) {
  return invoke<string>('read_profile_file', { index })
}

export async function saveProfileFile(index: string, fileData: string) {
  return (
    (
      await invoke<ValidationOutcome>('save_profile_file', {
        index,
        fileData,
      })
    ).status === 'valid'
  )
}

export async function importProfile(url: string, option?: IProfileOption) {
  return invoke<void>('import_profile', {
    url,
    option: option || { with_proxy: true },
  })
}

export async function reorderProfile(activeId: string, overId: string) {
  return invoke<void>('reorder_profile', {
    activeId,
    overId,
  })
}

export async function updateProfile(index: string, option?: IProfileOption) {
  return invoke<void>('update_profile', { index, option })
}

export async function deleteProfile(index: string) {
  return invoke<void>('delete_profile', { index })
}

export async function patchProfile(
  index: string,
  profile: Partial<IProfileItem>,
) {
  return invoke<void>('patch_profile', { index, profile })
}

export async function getClashInfo() {
  return invoke<IClashInfo | null>('get_clash_info')
}

// Fault-tolerant current proxy mode read (does not depend on mihomo /configs
// strict BaseConfig deserialization); used as a fallback for the home mode card.
export async function getClashMode() {
  return invoke<string | null>('get_clash_mode')
}

// Get runtime config which controlled by verge
export async function getRuntimeConfig() {
  return invoke<IConfigData | null>('get_runtime_config')
}

export async function getRuntimeYaml() {
  return invoke<string | null>('get_runtime_yaml')
}

export async function getRuntimeLogs() {
  return invoke<Record<string, [string, string][]>>('get_runtime_logs')
}

export async function getRuntimeProxyChainConfig(proxyChainExitNode: string) {
  return invoke<string>('get_runtime_proxy_chain_config', {
    proxyChainExitNode,
  })
}

export async function updateProxyChainConfigInRuntime(proxyChainConfig: any) {
  return invoke<void>('update_proxy_chain_config_in_runtime', {
    proxyChainConfig,
  })
}

export async function patchClashConfig(payload: Partial<IConfigData>) {
  return invoke<void>('patch_clash_config', { payload })
}

export async function patchClashMode(payload: string) {
  return invoke<void>('patch_clash_mode', { payload })
}

export async function syncTrayProxySelection() {
  return invoke<void>('sync_tray_proxy_selection')
}

/**
 * Record which node a group is on, in the current profile.
 *
 * Group and node, not the whole selection list: the merge happens in the backend against the
 * profile as it stands, so two selections made in quick succession cannot overwrite each other.
 */
export async function recordSelectedNode(groupName: string, node: string) {
  return invoke<void>('record_selected_node', { groupName, node })
}

export async function getProxyView(): Promise<ProxyViewV1> {
  const view = await invoke<ProxyViewV1>('get_proxy_view')
  if (view.schemaVersion !== 1) {
    throw new Error('Unsupported proxy view schema: ' + view.schemaVersion)
  }
  return view
}

export async function getClashLogs() {
  const regex = /time="(.+?)"\s+level=(.+?)\s+msg="(.+?)"/
  const newRegex = /(.+?)\s+(.+?)\s+(.+)/
  const logs = await invoke<string[]>('get_clash_logs')

  return logs.reduce<ILogItem[]>((acc, log) => {
    const result = log.match(regex)
    if (result) {
      const [_, _time, type, payload] = result
      const time = dayjs(_time).format('MM-DD HH:mm:ss')
      acc.push({ time, type, payload })
      return acc
    }

    const result2 = log.match(newRegex)
    if (result2) {
      const [_, time, type, payload] = result2
      acc.push({ time, type, payload })
    }
    return acc
  }, [])
}

export async function getVergeConfig() {
  return invoke<IVergeConfig>('get_verge_config')
}

export async function patchVergeConfig(payload: IVergeConfig) {
  return invoke<void>('patch_verge_config', { payload })
}

export async function getSystemProxy() {
  return invoke<{
    enable: boolean
    server: string
    bypass: string
  }>('get_sys_proxy')
}

export async function getAutotemProxy() {
  try {
    debugLog('[API] 开始调用 get_auto_proxy')
    const result = await invoke<{
      enable: boolean
      url: string
    }>('get_auto_proxy')
    debugLog('[API] get_auto_proxy 调用成功:', result)
    return result
  } catch (error) {
    console.error('[API] get_auto_proxy 调用失败:', error)
    return {
      enable: false,
      url: '',
    }
  }
}

export async function getEmbeddedServerPort() {
  return invoke<number>('get_embedded_server_port')
}

export async function changeClashCore(clashCore: string) {
  return invoke<string | null>('change_clash_core', { clashCore })
}

export async function restartCore() {
  return invoke<void>('restart_core')
}

export async function restartApp() {
  return invoke<void>('restart_app')
}

export async function getAppDir() {
  return invoke<string>('get_app_dir')
}

export async function openAppDir() {
  return invoke<void>('open_app_dir').catch((err) => showNotice.error(err))
}

export async function openCoreDir() {
  return invoke<void>('open_core_dir').catch((err) => showNotice.error(err))
}

export async function openLogsDir() {
  return invoke<void>('open_logs_dir').catch((err) => showNotice.error(err))
}

export const openWebUrl = async (url: string) => {
  try {
    await invoke('open_web_url', { url })
  } catch (err: any) {
    showNotice.error(err)
  }
}

export async function cmdTestDelay(url: string) {
  return invoke<number>('test_delay', { url })
}

export async function invoke_uwp_tool() {
  return invoke<void>('invoke_uwp_tool').catch((err) =>
    showNotice.error(err, 1500),
  )
}

export async function openDevTools() {
  return invoke('open_devtools')
}

export async function exitApp() {
  return invoke('exit_app')
}

export async function exportDiagnosticInfo() {
  return invoke('export_diagnostic_info')
}

interface SystemInfo {
  system_name: string
  system_version: string
  system_kernel_version: string
  system_arch: string
  app_version: string
  app_core_mode: string
  app_is_admin: boolean
}

export async function getSystemInfo() {
  return invoke<SystemInfo>('get_system_info')
}

export async function copyIconFile(
  path: string,
  name: 'common' | 'sysproxy' | 'tun',
) {
  const key = `icon_${name}_update_time`
  const previousTime = localStorage.getItem(key) || ''

  const currentTime = String(Date.now())
  localStorage.setItem(key, currentTime)

  const iconInfo = {
    name,
    previous_t: previousTime,
    current_t: currentTime,
  }

  return invoke<void>('copy_icon_file', { path, iconInfo })
}

export async function downloadIconCache(url: string, name: string) {
  return invoke<string>('download_icon_cache', { url, name })
}

export async function getNetworkInterfaces() {
  return invoke<string[]>('get_network_interfaces')
}

export async function getSystemHostname() {
  return invoke<string>('get_system_hostname')
}

export async function getNetworkInterfacesInfo() {
  return invoke<INetworkInterface[]>('get_network_interfaces_info')
}

export async function createWebdavBackup() {
  return invoke<void>('create_webdav_backup')
}

export async function createLocalBackup() {
  return invoke<void>('create_local_backup')
}

export async function deleteWebdavBackup(filename: string) {
  return invoke<void>('delete_webdav_backup', { filename })
}

export async function deleteLocalBackup(filename: string) {
  return invoke<void>('delete_local_backup', { filename })
}

export async function restoreWebDavBackup(filename: string) {
  return invoke<void>('restore_webdav_backup', { filename })
}

export async function restoreLocalBackup(filename: string) {
  return invoke<void>('restore_local_backup', { filename })
}

export async function importLocalBackup(source: string) {
  return invoke<string>('import_local_backup', { source })
}

export async function exportLocalBackup(filename: string, destination: string) {
  return invoke<void>('export_local_backup', { filename, destination })
}

export async function saveWebdavConfig(
  url: string,
  username: string,
  password: string,
) {
  return invoke<void>('save_webdav_config', {
    url,
    username,
    password,
  })
}

export async function listWebDavBackup() {
  const list: IWebDavFile[] = await invoke<IWebDavFile[]>('list_webdav_backup')
  list.forEach((item) => {
    item.filename = item.href.split('/').pop() as string
  })
  return list
}

export async function listLocalBackup() {
  return invoke<ILocalBackupFile[]>('list_local_backup')
}

// 获取当前运行模式
export type RunningMode = 'Service' | 'Sidecar' | 'NotRunning'

type ServiceHealth =
  | 'unknown'
  | 'ready'
  | 'notInstalled'
  | 'versionMismatch'
  | 'unavailable'

type PendingServiceAction =
  | 'install'
  | 'uninstall'
  | 'reinstall'
  | 'forceReinstall'

/**
 * How the core is running and what backs it, as one consistent snapshot.
 *
 * The derived answers travel with it — `tunCapable`, `serviceUsable`,
 * `serviceNeedsAttention` — so nothing here is recomputed from the raw fields.
 */
export interface RunState {
  mode: RunningMode
  service: ServiceHealth
  serviceUnavailableReason: string | null
  pendingAction: PendingServiceAction | null
  sidecarAllowed: boolean
  isAdmin: boolean
  opInFlight: boolean
  serviceUsable: boolean
  tunCapable: boolean
  serviceNeedsAttention: boolean
}

export const getRuntimeState = async () => {
  return invoke<RunState>('get_runtime_state')
}

// 获取应用运行时间
export const getAppUptime = async () => {
  return invoke<number>('get_app_uptime')
}

// 安装系统服务
export const installService = async () => {
  return invoke<void>('install_service')
}

// 卸载系统服务
export const uninstallService = async () => {
  return invoke<void>('uninstall_service')
}

export const reinstallService = async () => {
  return invoke<void>('reinstall_service')
}

export const repairService = async () => {
  return invoke<void>('repair_service')
}

export const continueWithSidecar = async () => {
  return invoke<void>('continue_with_sidecar')
}

export const entry_lightweight_mode = async () => {
  return invoke<void>('entry_lightweight_mode')
}

export async function getNextUpdateTime(uid: string) {
  return invoke<number | null>('get_next_update_time', { uid })
}

interface ToggleableProxyPort {
  enabled: boolean
  port: number
}

export interface ProxyPortSettings {
  mixedPort: number
  socks: ToggleableProxyPort
  http: ToggleableProxyPort
  redir: ToggleableProxyPort
  tproxy: ToggleableProxyPort
}

export type ListenerTransport = 'tcp' | 'udp'

export interface ListenerProbe {
  address: string
  transports: ListenerTransport[]
}

export type ListenerProbeOutcome =
  | { status: 'available' }
  | {
      status: 'conflict'
      port: number
      transport: ListenerTransport
    }
  | { status: 'invalid'; message: string }
  | { status: 'indeterminate'; message: string }

export type SaveProxyPortsOutcome =
  | { status: 'saved' }
  | { status: 'conflict'; port: number; transport: ListenerTransport }

export const probeListener = async (request: ListenerProbe) => {
  return invoke<ListenerProbeOutcome>('probe_listener', { request })
}

export const saveProxyPorts = async (settings: ProxyPortSettings) => {
  return invoke<SaveProxyPortsOutcome>('save_proxy_ports', { settings })
}
