import { invoke } from "@tauri-apps/api/core"
import { Store } from "@tauri-apps/plugin-store"

type MachineOs = {
  name: string
  version?: string | null
  architecture?: string | null
}

type MachineMetrics = {
  collectedAt: string
  cpuLogicalCores: number
  cpuPhysicalCores?: number | null
  cpuUsagePercent: number
  loadAverageOne?: number | null
  loadAverageFive?: number | null
  loadAverageFifteen?: number | null
  memoryTotalBytes: number
  memoryUsedBytes: number
  memoryUsedPercent: number
  uptimeSeconds: number
}

type MachineInventory = {
  cpuBrand?: string | null
  hostIdentifier?: string | null
}

type MachineProfile = {
  hostname: string
  os: MachineOs
  macAddresses: string[]
  serialNumbers: string[]
  inventory: MachineInventory
  metrics: MachineMetrics
}

type MachineRegisterResponse = {
  machineId: string
  tenantId?: string | null
  companyId?: string | null
  companySlug?: string | null
  machineToken: string
  machineEmail?: string | null
  expiresAt?: number | null
}

type AgentConfig = {
  machineId: string
  machineToken: string
  tenantId?: string | null
  companySlug?: string | null
  machineEmail?: string | null
  apiBaseUrl: string
  appUrl: string
  createdAt: number
  lastSyncedAt?: number | null
  expiresAt?: number | null
}

declare global {
  interface ImportMetaEnv {
    readonly VITE_APP_URL?: string
    readonly VITE_API_BASE_URL?: string
  }

  interface ImportMeta {
    readonly env: ImportMetaEnv
  }
}

const STORE_FILENAME = "machine-agent.json"
const DEFAULT_APP_URL = "http://localhost:3000"

function normalizeUrl(value?: string | null, fallback = DEFAULT_APP_URL) {
  const trimmed = (value ?? fallback).trim()
  if (!trimmed.startsWith("http")) {
    return fallback
  }
  return trimmed.replace(/\/+$/, "")
}

const appUrl = normalizeUrl(import.meta.env.VITE_APP_URL, DEFAULT_APP_URL)
const apiBaseUrl = normalizeUrl(import.meta.env.VITE_API_BASE_URL, appUrl)

const alertElement = document.getElementById("alert-container") as HTMLDivElement | null
const contentElement = document.getElementById("content") as HTMLDivElement | null
const statusElement = document.getElementById("status-text") as HTMLParagraphElement | null

function setAlert(message: string | null, variant: "info" | "error" | "success" = "info") {
  if (!alertElement) return
  if (!message) {
    alertElement.textContent = ""
    alertElement.className = "alert"
    return
  }
  alertElement.textContent = message
  const extra = variant === "info" ? "" : ` ${variant}`
  alertElement.className = `alert visible${extra}`
}

function setStatus(message: string) {
  if (statusElement) {
    statusElement.textContent = message
  }
}

let storeInstance: Store | null = null

async function ensureStoreLoaded(): Promise<Store> {
  if (!storeInstance) {
    try {
      storeInstance = await Store.load(STORE_FILENAME)
    } catch (error) {
      console.error("[agent] Falha ao carregar store", error)
      throw error
    }
  }
  return storeInstance
}

async function loadConfig(): Promise<AgentConfig | null> {
  try {
    const store = await ensureStoreLoaded()
    const record = await store.get<AgentConfig>("config")
    if (!record) return null
    return record
  } catch (error) {
    console.error("[agent] Falha ao recuperar configuração", error)
    return null
  }
}

async function saveConfig(config: AgentConfig) {
  const store = await ensureStoreLoaded()
  await store.set("config", config)
  await store.save()
}

async function clearConfig() {
  const store = await ensureStoreLoaded()
  await store.delete("config")
  await store.save()
}

async function collectMachineProfile(): Promise<MachineProfile> {
  return await invoke<MachineProfile>("collect_machine_profile")
}

async function startHeartbeat(config: AgentConfig) {
  await invoke("start_machine_agent", {
    baseUrl: config.apiBaseUrl,
    token: config.machineToken,
    status: "online",
    intervalSeconds: 300,
  })
}

async function stopHeartbeat() {
  await invoke("stop_machine_agent")
}

function formatBytes(bytes: number) {
  if (!bytes || Number.isNaN(bytes)) return "—"
  const units = ["B", "KB", "MB", "GB", "TB"]
  let value = bytes
  let index = 0
  while (value >= 1024 && index < units.length - 1) {
    value /= 1024
    index += 1
  }
  return `${value.toFixed(value >= 10 || index === 0 ? 0 : 1)} ${units[index]}`
}

function formatPercent(value: number) {
  if (Number.isNaN(value)) return "—"
  return `${value.toFixed(1)}%`
}

function formatDate(timestamp?: number | null) {
  if (!timestamp) return "—"
  try {
    return new Date(timestamp).toLocaleString()
  } catch {
    return "—"
  }
}

function renderMachineSummary(profile: MachineProfile) {
  if (!contentElement) return
  const macs = profile.macAddresses.length > 0 ? profile.macAddresses.join(", ") : "—"
  const serials = profile.serialNumbers.length > 0 ? profile.serialNumbers.join(", ") : "—"
  const metrics = profile.metrics
  const lastCollection = metrics.collectedAt ? new Date(metrics.collectedAt).toLocaleString() : "—"

  return `
    <div class="machine-summary">
      <div><strong>Hostname:</strong> ${profile.hostname}</div>
      <div><strong>Sistema:</strong> ${profile.os.name}${profile.os.version ? ` ${profile.os.version}` : ""} (${profile.os.architecture ?? "?"})</div>
      <div><strong>Endereços MAC:</strong> ${macs}</div>
      <div><strong>Identificadores:</strong> ${serials}</div>
      <div><strong>CPU:</strong> ${metrics.cpuPhysicalCores ?? metrics.cpuLogicalCores} núcleos · uso ${formatPercent(metrics.cpuUsagePercent)}</div>
      <div><strong>Memória:</strong> ${formatBytes(metrics.memoryUsedBytes)} / ${formatBytes(metrics.memoryTotalBytes)} (${formatPercent(metrics.memoryUsedPercent)})</div>
      <div><strong>Coletado em:</strong> ${lastCollection}</div>
    </div>
  `
}

function renderRegistered(config: AgentConfig) {
  if (!contentElement) return
  const summaryHtml = `
    <div class="machine-summary">
      <div><strong>ID da máquina:</strong> ${config.machineId}</div>
      <div><strong>Email vinculado:</strong> ${config.machineEmail ?? "—"}</div>
      <div><strong>Tenant:</strong> ${config.tenantId ?? "padrão"}</div>
      <div><strong>Empresa:</strong> ${config.companySlug ?? "não vinculada"}</div>
      <div><strong>Token expira em:</strong> ${formatDate(config.expiresAt)}</div>
      <div><strong>Última sincronização:</strong> ${formatDate(config.lastSyncedAt)}</div>
      <div><strong>Ambiente:</strong> ${config.appUrl}</div>
    </div>
  `

  contentElement.innerHTML = `
    <p>Esta máquina já está provisionada e com heartbeat ativo.</p>
    ${summaryHtml}
    <div class="actions">
      <button id="open-app">Abrir sistema</button>
      <button class="secondary" id="reset-agent">Reprovisionar</button>
    </div>
  `

  const openButton = document.getElementById("open-app")
  const resetButton = document.getElementById("reset-agent")

  openButton?.addEventListener("click", () => redirectToApp(config))
  resetButton?.addEventListener("click", async () => {
    await stopHeartbeat().catch(() => undefined)
    await clearConfig()
    setAlert("Configuração removida. Reiniciando fluxo de provisionamento.", "success")
    setTimeout(() => window.location.reload(), 600)
  })

  setStatus("Máquina provisionada. Redirecionando para a interface web…")
  setTimeout(() => redirectToApp(config), 1500)
}

function renderProvisionForm(profile: MachineProfile) {
  if (!contentElement) return

  const summary = renderMachineSummary(profile) ?? ""

  contentElement.innerHTML = `
    <form id="provision-form">
      <label>
        Código de provisionamento
        <input type="password" name="provisioningSecret" placeholder="Insira o código fornecido" required autocomplete="one-time-code" />
      </label>
      <label>
        Tenant (opcional)
        <span class="optional">Use apenas se houver múltiplos ambientes</span>
        <input type="text" name="tenantId" placeholder="Ex.: tenant-atlas" />
      </label>
      <label>
        Empresa (slug opcional)
        <span class="optional">Informe para vincular à empresa correta</span>
        <input type="text" name="companySlug" placeholder="Ex.: empresa-exemplo" />
      </label>
      <div class="actions">
        <button type="submit">Registrar máquina</button>
        <button type="button" class="secondary" id="refresh-profile">Atualizar dados</button>
      </div>
    </form>
    ${summary}
  `

  const form = document.getElementById("provision-form") as HTMLFormElement | null
  const refreshButton = document.getElementById("refresh-profile")

  form?.addEventListener("submit", (event) => {
    event.preventDefault()
    handleRegister(profile, form)
  })

  refreshButton?.addEventListener("click", async () => {
    setStatus("Recolhendo informações atualizadas da máquina…")
    try {
      const updatedProfile = await collectMachineProfile()
      renderProvisionForm(updatedProfile)
      setStatus("Dados atualizados. Revise e confirme o provisionamento.")
    } catch (error) {
      console.error("[agent] Falha ao atualizar perfil da máquina", error)
      setAlert("Não foi possível atualizar as informações da máquina.", "error")
    }
  })
}

async function handleRegister(profile: MachineProfile, form: HTMLFormElement) {
  const submitButton = form.querySelector("button[type=submit]") as HTMLButtonElement | null
  const formData = new FormData(form)
  const provisioningSecret = (formData.get("provisioningSecret") as string | null)?.trim()
  const tenantId = (formData.get("tenantId") as string | null)?.trim()
  const companySlug = (formData.get("companySlug") as string | null)?.trim()

  if (!provisioningSecret) {
    setAlert("Informe o código de provisionamento.", "error")
    return
  }

  try {
    if (submitButton) {
      submitButton.disabled = true
    }
    setAlert(null)
    setStatus("Enviando dados de registro da máquina…")

    const payload = {
      provisioningSecret,
      tenantId: tenantId && tenantId.length > 0 ? tenantId : undefined,
      companySlug: companySlug && companySlug.length > 0 ? companySlug : undefined,
      hostname: profile.hostname,
      os: profile.os,
      macAddresses: profile.macAddresses,
      serialNumbers: profile.serialNumbers,
      metadata: {
        inventory: profile.inventory,
        metrics: profile.metrics,
      },
      registeredBy: "desktop-agent",
    }

    const response = await fetch(`${apiBaseUrl}/api/machines/register`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    })

    if (!response.ok) {
      let message = `Falha ao registrar máquina (${response.status})`
      try {
        const errorBody = await response.json()
        if (errorBody?.error) {
          message = errorBody.error
        }
      } catch {
        // ignore
      }
      throw new Error(message)
    }

    const data = (await response.json()) as MachineRegisterResponse
    const config: AgentConfig = {
      machineId: data.machineId,
      machineToken: data.machineToken,
      tenantId: data.tenantId ?? null,
      companySlug: data.companySlug ?? null,
      machineEmail: data.machineEmail ?? null,
      apiBaseUrl,
      appUrl,
      createdAt: Date.now(),
      lastSyncedAt: Date.now(),
      expiresAt: data.expiresAt ?? null,
    }

    await saveConfig(config)
    await startHeartbeat(config)

    setAlert("Máquina registrada com sucesso! Abrindo a interface web…", "success")
    setStatus("Autenticando dispositivo e abrindo o Sistema de Chamados.")

    setTimeout(() => redirectToApp(config), 800)
  } catch (error) {
    console.error("[agent] Erro no registro da máquina", error)
    const message = error instanceof Error ? error.message : "Erro desconhecido ao registrar a máquina."
    const normalized = message.toLowerCase()
    if (normalized.includes("failed to fetch") || normalized.includes("load failed") || normalized.includes("network")) {
      setAlert("Não foi possível se conectar ao servidor. Verifique a conexão e o endereço configurado.", "error")
    } else if (normalized.includes("401") || normalized.includes("403") || normalized.includes("código de provisionamento inválido")) {
      setAlert("Código de provisionamento inválido. Confirme o segredo configurado no servidor e tente novamente.", "error")
    } else {
      setAlert(message, "error")
    }
    setStatus("Revise os dados e tente novamente.")
    if (submitButton) {
      submitButton.disabled = false
    }
  }
}

function redirectToApp(config: AgentConfig) {
  const url = `${config.appUrl}/machines/handshake?token=${encodeURIComponent(config.machineToken)}`
  window.location.replace(url)
}

async function ensureHeartbeat(config: AgentConfig): Promise<AgentConfig> {
  const adjustedConfig = {
    ...config,
    apiBaseUrl,
    appUrl,
    lastSyncedAt: Date.now(),
  }

  await saveConfig(adjustedConfig)
  await startHeartbeat(adjustedConfig)

  return adjustedConfig
}

async function bootstrap() {
  setStatus("Iniciando agente desktop…")
  setAlert(null)

  try {
    const stored = await loadConfig()
    if (stored?.machineToken) {
      const updated = await ensureHeartbeat(stored)
      renderRegistered(updated)
      return
    }
  } catch (error) {
    console.error("[agent] Falha ao iniciar com configuração existente", error)
    setAlert("Não foi possível carregar a configuração armazenada. Você poderá reprovisionar abaixo.", "error")
  }

  try {
    setStatus("Coletando informações básicas da máquina…")
    const profile = await collectMachineProfile()
    renderProvisionForm(profile)
    setStatus("Informe o código de provisionamento para registrar esta máquina.")
  } catch (error) {
    console.error("[agent] Falha ao coletar dados da máquina", error)
    setAlert("Não foi possível coletar dados da máquina. Verifique permissões do sistema e tente novamente.", "error")
    setStatus("Interação necessária para continuar.")
  }
}

document.addEventListener("DOMContentLoaded", () => {
  void bootstrap()
})
