import { defineStore } from 'pinia'
import { apiFetch } from '../services/api'
import type {
  OpenClawAdoptionInventory,
  OpenClawDiscovery,
  OpenClawProbe,
  OpenClawSetupOperation,
  OpenClawSetupStatus,
} from '../types/openclaw-setup'

async function readJson<T>(path: string, init?: RequestInit): Promise<T> {
  const response = await apiFetch(path, init)
  const payload = await response.json().catch(() => null) as
    | T
    | { detail?: string; message?: string; title?: string; recovery?: string }
    | null
  if (!response.ok) {
    const failure = payload as {
      detail?: string
      message?: string
      title?: string
      recovery?: string
    } | null
    const message = failure?.detail
      || failure?.message
      || failure?.title
      || `OpenClaw setup request failed with HTTP ${response.status}`
    throw new Error(
      failure?.recovery ? `${message} ${failure.recovery}` : message,
    )
  }
  return payload as T
}

export const useOpenClawSetupStore = defineStore('openclaw-setup', {
  state: () => ({
    status: null as OpenClawSetupStatus | null,
    discovery: null as OpenClawDiscovery | null,
    probeResult: null as OpenClawProbe | null,
    inventory: null as OpenClawAdoptionInventory | null,
    loading: false,
    action: '' as '' | 'discover' | 'probe' | 'attach' | 'verify' | 'adopt' | 'management' | 'remove',
    error: '',
    announcement: '',
  }),
  getters: {
    busy: state => state.loading || Boolean(state.action),
    revision: state => state.status?.revision ?? null,
  },
  actions: {
    async load() {
      this.loading = true
      this.error = ''
      try {
        this.status = await readJson<OpenClawSetupStatus>('/api/v1/openclaw/setup')
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'OpenClaw setup status could not be loaded.'
      } finally {
        this.loading = false
      }
    },
    async discover(includeMdns = false) {
      this.action = 'discover'
      this.error = ''
      try {
        this.discovery = await readJson<OpenClawDiscovery>('/api/v1/openclaw/setup/discover', {
          method: 'POST',
          body: JSON.stringify({ includeMdns }),
        })
        this.announcement = `${this.discovery.candidates.length} OpenClaw candidate(s) found.`
        return this.discovery
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Discovery failed.'
        throw error
      } finally {
        this.action = ''
      }
    },
    async probe(endpoint: string, tlsCertificateFingerprint: string | null) {
      this.action = 'probe'
      this.error = ''
      try {
        const response = await apiFetch('/api/v1/openclaw/setup/probe', {
          method: 'POST',
          body: JSON.stringify({ endpoint, tlsCertificateFingerprint }),
        })
        const operation = await response.json().catch(() => null) as OpenClawSetupOperation<OpenClawProbe> | null
        if (!operation) throw new Error(`OpenClaw probe failed with HTTP ${response.status}`)
        if (operation.data) this.probeResult = operation.data
        this.announcement = operation.message
        if (!response.ok || !operation.ok) {
          this.error = operation.recovery || operation.message
          return operation.data
        }
        if (!operation.data) throw new Error('OpenClaw probe returned no evidence.')
        return operation.data
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Probe failed.'
        throw error
      } finally {
        this.action = ''
      }
    },
    async attach(input: {
      endpoint: string
      discoverySource: string
      tlsCertificateFingerprint: string | null
      bootstrapToken: string | null
      bootstrapSecretReference: string | null
    }) {
      this.action = 'attach'
      this.error = ''
      try {
        const operation = await readJson<OpenClawSetupOperation<OpenClawSetupStatus>>(
          '/api/v1/openclaw/setup/attach',
          { method: 'POST', body: JSON.stringify(input) },
        )
        if (!operation.ok) throw new Error(operation.recovery || operation.message)
        if (operation.data) this.status = operation.data
        this.announcement = operation.message
        await this.load()
        return operation
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Attach failed.'
        throw error
      } finally {
        this.action = ''
      }
    },
    async verify() {
      if (this.revision === null) throw new Error('No setup revision is available.')
      this.action = 'verify'
      this.error = ''
      try {
        const operation = await readJson<OpenClawSetupOperation<OpenClawSetupStatus>>(
          '/api/v1/openclaw/setup/verify',
          { method: 'POST', body: JSON.stringify({ expectedRevision: this.revision }) },
        )
        if (!operation.ok) throw new Error(operation.recovery || operation.message)
        if (operation.data) this.status = operation.data
        this.announcement = operation.message
        await this.load()
        return operation
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Verification failed.'
        throw error
      } finally {
        this.action = ''
      }
    },
    async adopt() {
      if (this.revision === null) throw new Error('No setup revision is available.')
      this.action = 'adopt'
      this.error = ''
      try {
        const operation = await readJson<OpenClawSetupOperation<OpenClawAdoptionInventory>>(
          '/api/v1/openclaw/setup/adopt',
          { method: 'POST', body: JSON.stringify({ expectedRevision: this.revision }) },
        )
        if (!operation.ok) throw new Error(operation.recovery || operation.message)
        this.inventory = operation.data
        this.announcement = operation.message
        await this.load()
        return operation
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Adoption failed.'
        throw error
      } finally {
        this.action = ''
      }
    },
    async setManagement(enabled: boolean) {
      if (this.revision === null) throw new Error('No setup revision is available.')
      this.action = 'management'
      this.error = ''
      try {
        const operation = await readJson<OpenClawSetupOperation<OpenClawSetupStatus>>(
          '/api/v1/openclaw/setup/management',
          {
            method: 'POST',
            body: JSON.stringify({
              enabled,
              confirmed: true,
              expectedRevision: this.revision,
            }),
          },
        )
        if (!operation.ok) throw new Error(operation.recovery || operation.message)
        if (operation.data) this.status = operation.data
        this.announcement = operation.message
        await this.load()
        return operation
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Management policy could not be changed.'
        throw error
      } finally {
        this.action = ''
      }
    },
    async remove(endpoint: string, deviceId: string | null) {
      if (this.revision === null) throw new Error('No setup revision is available.')
      this.action = 'remove'
      this.error = ''
      try {
        const operation = await readJson<OpenClawSetupOperation<OpenClawSetupStatus>>(
          '/api/v1/openclaw/setup/connection',
          {
            method: 'DELETE',
            body: JSON.stringify({
              endpoint,
              deviceId,
              expectedRevision: this.revision,
            }),
          },
        )
        if (!operation.ok) throw new Error(operation.recovery || operation.message)
        this.announcement = operation.recovery
          ? `${operation.message} ${operation.recovery}`
          : operation.message
        this.discovery = null
        this.probeResult = null
        this.inventory = null
        await this.load()
        return operation
      } catch (error) {
        this.error = error instanceof Error ? error.message : 'Connection could not be removed.'
        throw error
      } finally {
        this.action = ''
      }
    },
  },
})
