/**
 * Auth store: manages Duniter Ed25519 challenge-response authentication.
 *
 * Persists the session token in localStorage for SPA rehydration.
 * The identity object mirrors the backend IdentityOut schema.
 */

/**
 * Sign a challenge using the injected Duniter/Substrate wallet extension
 * (Cesium2, polkadot.js extension, Talisman, etc.).
 *
 * The extension signs <Bytes>{challenge}</Bytes> to match the backend verifier.
 */
// TODO: trustWallet — remplacer par postMessage vers l'iframe trustWallet (librodrome)
// Protocole prévu : window.postMessage({ type: 'LD_SIGN_REQUEST', address, challenge })
// → trustWallet répond { type: 'LD_SIGN_RESPONSE', signature }
async function _signWithExtension(address: string, challenge: string): Promise<string> {
  const { web3Enable, web3FromAddress } = await import('@polkadot/extension-dapp')
  const { stringToHex } = await import('@polkadot/util')

  const extensions = await web3Enable('libreDecision')
  if (!extensions.length) {
    throw new Error('Aucune extension Duniter détectée. Installez Cesium² ou Polkadot.js.')
  }

  let injector
  try {
    injector = await web3FromAddress(address)
  } catch {
    throw new Error(`Adresse ${address.slice(0, 10)}… introuvable dans l'extension.`)
  }

  if (!injector.signer?.signRaw) {
    throw new Error("L'extension ne supporte pas la signature de messages bruts.")
  }

  const { signature } = await injector.signer.signRaw({
    address,
    data: stringToHex(challenge),
    type: 'bytes',
  })
  return signature
}

export interface DuniterIdentity {
  id: string
  address: string
  display_name: string | null
  wot_status: string
  is_smith: boolean
  is_techcomm: boolean
}

interface AuthState {
  token: string | null
  identity: DuniterIdentity | null
  loading: boolean
  error: string | null
}

export const useAuthStore = defineStore('auth', {
  state: (): AuthState => ({
    token: null,
    identity: null,
    loading: false,
    error: null,
  }),

  getters: {
    isAuthenticated: (state): boolean => !!state.token && !!state.identity,
    isSmith: (state): boolean => state.identity?.is_smith ?? false,
    isTechComm: (state): boolean => state.identity?.is_techcomm ?? false,
    displayName: (state): string => {
      if (!state.identity) return ''
      return state.identity.display_name || state.identity.address.slice(0, 12) + '...'
    },
  },

  actions: {
    /**
     * Initiate the challenge-response login flow.
     *
     * Steps:
     * 1. POST /auth/challenge with the Duniter SS58 address
     * 2. Client signs the challenge with Ed25519 private key
     * 3. POST /auth/verify with address + signature + challenge
     * 4. Store the returned token and identity
     */
    async login(address: string, signFn?: (challenge: string) => Promise<string>) {
      this.loading = true
      this.error = null

      try {
        const { $api } = useApi()

        // Step 1: Request challenge
        const challengeRes = await $api<{ challenge: string; expires_at: string }>(
          '/auth/challenge',
          {
            method: 'POST',
            body: { address },
          },
        )

        // Step 2: Sign the challenge via polkadot.js / Cesium2 extension
        let signature: string
        if (signFn) {
          signature = await signFn(challengeRes.challenge)
        } else {
          signature = await _signWithExtension(address, challengeRes.challenge)
        }

        // Step 3: Verify and get token
        const verifyRes = await $api<{ token: string; identity: DuniterIdentity }>(
          '/auth/verify',
          {
            method: 'POST',
            body: {
              address,
              signature,
              challenge: challengeRes.challenge,
            },
          },
        )

        // Step 4: Store credentials
        this.token = verifyRes.token
        this.identity = verifyRes.identity
        this._persistToken()

        return verifyRes
      } catch (err: any) {
        this.error = err?.data?.detail || err?.message || 'Erreur de connexion'
        throw err
      } finally {
        this.loading = false
      }
    },

    /**
     * Fetch the currently authenticated identity from the backend.
     * Used on app init to validate a persisted token.
     */
    async fetchMe() {
      if (!this.token) return

      this.loading = true
      this.error = null

      try {
        const { $api } = useApi()
        const identity = await $api<DuniterIdentity>('/auth/me')
        this.identity = identity
      } catch (err: any) {
        const status = (err as any)?.status ?? 0
        this.error = err?.message || 'Session invalide'
        // N'effacer le token que sur 401/403 (session réellement invalide)
        // Les erreurs réseau ou 5xx sont transitoires — conserver la session
        if (status === 401 || status === 403) {
          this.token = null
          this.identity = null
          this._clearToken()
        }
        throw err
      } finally {
        this.loading = false
      }
    },

    /**
     * Log out: invalidate session on server and clear local state.
     */
    async logout() {
      try {
        if (this.token) {
          const { $api } = useApi()
          await $api('/auth/logout', { method: 'POST' })
        }
      } catch {
        // Ignore errors during logout -- clear local state regardless
      } finally {
        this.token = null
        this.identity = null
        this.error = null
        this._clearToken()
        navigateTo('/login')
      }
    },

    /**
     * Hydrate the token from localStorage on app init.
     */
    hydrateFromStorage() {
      if (import.meta.client) {
        const stored = localStorage.getItem('libredecision_token')
        if (stored) {
          this.token = stored
        }
      }
    },

    /** @internal Persist token to localStorage */
    _persistToken() {
      if (import.meta.client && this.token) {
        localStorage.setItem('libredecision_token', this.token)
      }
    },

    /** @internal Clear token from localStorage */
    _clearToken() {
      if (import.meta.client) {
        localStorage.removeItem('libredecision_token')
      }
    },
  },
})

// Note: hydration from localStorage happens in app.vue onMounted
// via auth.hydrateFromStorage() before calling auth.fetchMe().
