import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useApi } from '@/composables/useApi'
import type { FleetData, FleetInstance } from '@/stores/fleet'

/** A game instance enriched with its host context, flattened from the fleet. */
export interface ManagedInstance extends FleetInstance {
  host_id: string
  host_hostname: string
  host_status: string
}

type LifecycleAction = 'start' | 'stop' | 'restart' | 'status' | 'steam_update'

/**
 * Instance management — the Server page operates on a selected game instance
 * (not the legacy single-server connection). Reads the fleet to enumerate
 * instances and drives the per-instance command bridge
 * (POST /api/instances/:id/lifecycle | /rcon).
 */
export const useInstancesStore = defineStore('instances', () => {
  const instances = ref<ManagedInstance[]>([])
  const currentId = ref<string | null>(null)
  const loading = ref(false)
  const acting = ref<LifecycleAction | null>(null)
  const error = ref<string | null>(null)

  const api = useApi()

  const current = computed<ManagedInstance | null>(
    () => instances.value.find((i) => i.id === currentId.value) ?? null,
  )

  /** Fetch the fleet and flatten its instances. Optionally prefer a game. */
  async function fetchInstances(preferGame?: string): Promise<void> {
    loading.value = true
    error.value = null
    try {
      const data = await api.get<FleetData>('/fleet')
      const flat: ManagedInstance[] = []
      for (const host of data.hosts) {
        for (const inst of host.instances) {
          flat.push({
            ...inst,
            host_id: host.id,
            host_hostname: host.hostname,
            host_status: host.status,
          })
        }
      }
      instances.value = flat
      // Keep the current selection if it still exists; else prefer the active
      // game, else the first instance.
      if (!flat.some((i) => i.id === currentId.value)) {
        const preferred = preferGame ? flat.find((i) => i.game === preferGame) : undefined
        currentId.value = (preferred ?? flat[0])?.id ?? null
      }
    } catch (e) {
      console.error('Failed to fetch instances:', e)
      error.value = e instanceof Error ? e.message : 'Failed to load instances'
    } finally {
      loading.value = false
    }
  }

  function select(id: string): void {
    currentId.value = id
  }

  /**
   * Send a lifecycle command to the current instance and apply the agent's
   * reply state OPTIMISTICALLY. The reply is authoritative for the action just
   * taken; the fleet DB only catches up on the next heartbeat (~10s), so an
   * immediate refetch would read a stale state and clobber the result.
   * Throws on failure so the view can toast.
   */
  async function lifecycle(action: LifecycleAction): Promise<Record<string, unknown>> {
    const id = currentId.value
    if (!id) throw new Error('No instance selected')
    acting.value = action
    try {
      const res = await api.post<Record<string, unknown>>(`/instances/${id}/lifecycle`, { action })
      applyReplyState(id, res)
      return res
    } finally {
      acting.value = null
    }
  }

  /** Update an instance's state/uptime from a lifecycle/status reply. */
  function applyReplyState(id: string, res: Record<string, unknown>): void {
    if ((res as { status?: string }).status !== 'success') return
    const stateObj = (res as { state?: { state?: string } }).state
    const newState = stateObj?.state
    const inst = instances.value.find((i) => i.id === id)
    if (inst && typeof newState === 'string') {
      inst.state = newState
      const up = (res as { uptime_seconds?: number }).uptime_seconds
      inst.uptime_seconds = typeof up === 'number' ? up : newState === 'running' ? inst.uptime_seconds : 0
    }
  }

  async function rcon(command: string): Promise<Record<string, unknown>> {
    const id = currentId.value
    if (!id) throw new Error('No instance selected')
    return api.post<Record<string, unknown>>(`/instances/${id}/rcon`, { command })
  }

  /** Read a config/text file from the current instance (jailed to its root). */
  async function readFile(path: string): Promise<string> {
    const id = currentId.value
    if (!id) throw new Error('No instance selected')
    const res = await api.get<{ content?: string }>(
      `/instances/${id}/file?path=${encodeURIComponent(path)}`,
    )
    return res?.content ?? ''
  }

  /** Write a config/text file to the current instance. */
  async function writeFile(path: string, content: string): Promise<void> {
    const id = currentId.value
    if (!id) throw new Error('No instance selected')
    await api.put(`/instances/${id}/file`, { path, content })
  }

  return {
    instances,
    currentId,
    current,
    loading,
    acting,
    error,
    fetchInstances,
    select,
    lifecycle,
    rcon,
    readFile,
    writeFile,
  }
})
