import { defineStore } from 'pinia'

export interface AuthUser {
  id: string
  email: string
  displayName: string
  role: string
}

interface AuthPayload {
  accessToken: string
  expiresAt: string
  user: AuthUser
}

interface LoginErrorInfo {
  message: string
  remaining: number
  retryAfterSeconds: number
}

let refreshInFlight: Promise<boolean> | null = null

export const useAuthStore = defineStore('auth', {
  state: () => ({
    accessToken: null as string | null,
    expiresAt: null as string | null,
    user: null as AuthUser | null,
    initialized: false,
    loading: false,
    /** Remaining login attempts in the current window (null = unknown) */
    remainingAttempts: null as number | null,
    /** Seconds until rate-limit reset (0 = not rate-limited) */
    retryAfterSeconds: 0,
  }),
  getters: {
    isAuthenticated: state => Boolean(state.accessToken && state.user),
    isOwner: state => state.user?.role.toLowerCase() === 'owner',
    isRateLimited: state => state.remainingAttempts === 0 && state.retryAfterSeconds > 0,
    /** Returns true if the current web-ui user is Iris (JWT user identity matches "iris"). */
    isIris: state => {
      if (!state.user) return false
      const lower = state.user.email.toLowerCase()
      return lower.includes('iris') || state.user.displayName.toLowerCase().includes('iris')
    },
    /** Returns true if the current web-ui user is Bao (JWT user identity matches "bao"). */
    isBao: state => {
      if (!state.user) return false
      const lower = state.user.email.toLowerCase()
      return lower.includes('bao') || state.user.displayName.toLowerCase().includes('bao')
    },
  },
  actions: {
    applySession(payload: AuthPayload) {
      this.accessToken = payload.accessToken
      this.expiresAt = payload.expiresAt
      this.user = payload.user
      this.remainingAttempts = null
      this.retryAfterSeconds = 0
    },
    clearSession() {
      this.accessToken = null
      this.expiresAt = null
      this.user = null
      this.remainingAttempts = null
      this.retryAfterSeconds = 0
    },
    async initialize() {
      // Router guards can overlap during the initial navigation. Reuse the
      // active refresh instead of treating the not-yet-applied session as an
      // unauthenticated result.
      if (this.initialized) {
        return refreshInFlight ?? this.isAuthenticated
      }
      this.initialized = true
      return this.refresh()
    },
    async login(email: string, password: string): Promise<void> {
      this.loading = true
      this.remainingAttempts = null
      this.retryAfterSeconds = 0
      try {
        const response = await fetch('/api/v1/auth/login', {
          method: 'POST',
          credentials: 'include',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ email, password }),
        })

        // Try to parse remaining from headers
        const remainingHeader = response.headers.get('X-RateLimit-Remaining')
        if (remainingHeader !== null) {
          this.remainingAttempts = parseInt(remainingHeader, 10)
        }

        const resetHeader = response.headers.get('X-RateLimit-Reset')
        if (resetHeader !== null) {
          const resetTs = parseInt(resetHeader, 10) * 1000
          this.retryAfterSeconds = Math.max(0, Math.ceil((resetTs - Date.now()) / 1000))
        }

        if (!response.ok) {
          // Try to parse structured JSON body for rate-limit info
          let remaining = this.remainingAttempts
          let retryAfter = this.retryAfterSeconds

          try {
            const body = await response.json() as Record<string, unknown>
            if (typeof body.remaining === 'number') remaining = body.remaining
            if (typeof body.retryAfterSeconds === 'number') retryAfter = body.retryAfterSeconds
            const problemMessage = typeof body.detail === 'string'
              ? body.detail
              : typeof body.message === 'string'
                ? body.message
                : typeof body.title === 'string'
                  ? body.title
                  : null

            if (response.status === 429) {
              this.remainingAttempts = 0
              this.retryAfterSeconds = retryAfter
              throw new LoginError(problemMessage || 'Too many attempts.', 0, retryAfter)
            } else if (response.status === 401) {
              this.remainingAttempts = remaining
              this.retryAfterSeconds = retryAfter
              throw new LoginError(
                problemMessage || 'Invalid email or password.',
                remaining ?? 4,
                retryAfter,
              )
            }
          } catch (error) {
            if (error instanceof LoginError) throw error
            // Fallback for non-JSON error responses
          }

          if (response.status === 429) {
            this.remainingAttempts = 0
            const retryAfterSec = this.retryAfterSeconds || 60
            this.retryAfterSeconds = retryAfterSec
            throw new LoginError('Too many attempts. Please wait.', 0, retryAfterSec)
          }

          throw new LoginError('Invalid email or password.', this.remainingAttempts ?? 4, this.retryAfterSeconds)
        }

        this.applySession(await response.json() as AuthPayload)
      } finally {
        this.loading = false
      }
    },
    async refresh(): Promise<boolean> {
      if (refreshInFlight) return refreshInFlight

      refreshInFlight = (async () => {
        try {
          const response = await fetch('/api/v1/auth/refresh', {
            method: 'POST',
            credentials: 'include',
          })
          if (!response.ok) {
            this.clearSession()
            return false
          }

          this.applySession(await response.json() as AuthPayload)
          return true
        } catch {
          this.clearSession()
          return false
        } finally {
          refreshInFlight = null
        }
      })()

      return refreshInFlight
    },
    async logout() {
      try {
        await fetch('/api/v1/auth/logout', {
          method: 'POST',
          credentials: 'include',
          headers: this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : undefined,
        })
      } finally {
        this.clearSession()
      }
    },
  },
})

/** Custom error carrying rate-limit metadata. */
class LoginError extends Error {
  remaining: number
  retryAfterSeconds: number

  constructor(message: string, remaining: number, retryAfterSeconds: number) {
    super(message)
    this.name = 'LoginError'
    this.remaining = remaining
    this.retryAfterSeconds = retryAfterSeconds
  }
}
