import { getExplorerApiBase } from './blockscout'

declare global {
  interface Window {
    ethereum?: {
      request: (args: { method: string; params?: unknown[] | object }) => Promise<unknown>
    }
  }
}

export interface AccessUser {
  id: string
  email: string
  username: string
  is_admin?: boolean
}

export interface AccessSession {
  user: AccessUser
  token: string
  expires_at: string
}

export interface WalletAccessSession {
  token: string
  expiresAt: string
  track: string
  permissions: string[]
  address: string
}

export interface AccessProduct {
  slug: string
  name: string
  provider: string
  vmid: number
  http_url: string
  ws_url?: string
  default_tier: string
  requires_approval: boolean
  billing_model: string
  description: string
  use_cases: string[]
  management_features: string[]
}

export interface AccessAPIKeyRecord {
  id: string
  name: string
  tier: string
  productSlug: string
  scopes: string[]
  monthlyQuota: number
  requestsUsed: number
  approved: boolean
  approvedAt?: string | null
  rateLimitPerSecond: number
  rateLimitPerMinute: number
  lastUsedAt?: string | null
  expiresAt?: string | null
  revoked: boolean
  createdAt: string
}

export interface CreateAccessAPIKeyRequest {
  name: string
  tier: string
  productSlug: string
  expiresDays?: number
  monthlyQuota?: number
  scopes?: string[]
}

export interface AccessSubscription {
  id: string
  productSlug: string
  tier: string
  status: string
  monthlyQuota: number
  requestsUsed: number
  requiresApproval: boolean
  approvedAt?: string | null
  approvedBy?: string | null
  notes?: string | null
  createdAt: string
}

export interface AccessUsageSummary {
  product_slug: string
  active_keys: number
  requests_used: number
  monthly_quota: number
}

export interface AccessAuditEntry {
  id: number
  apiKeyId: string
  keyName: string
  productSlug: string
  methodName: string
  requestCount: number
  lastIp?: string | null
  createdAt: string
}

const ACCESS_TOKEN_STORAGE_KEY = 'explorer_access_token'
const WALLET_SESSION_STORAGE_KEY = 'explorer_wallet_session'
const ACCESS_SESSION_EVENT = 'explorer-access-session-changed'
const ACCESS_API_PREFIX = '/explorer-api/v1'

function getStoredAccessToken(): string {
  if (typeof window === 'undefined') return ''
  return window.localStorage.getItem(ACCESS_TOKEN_STORAGE_KEY) || getStoredWalletSession()?.token || ''
}

function setStoredAccessToken(token: string) {
  if (typeof window === 'undefined') return
  if (token) {
    window.localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, token)
  } else {
    window.localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY)
  }
  window.dispatchEvent(new Event(ACCESS_SESSION_EVENT))
}

function getStoredWalletSession(): WalletAccessSession | null {
  if (typeof window === 'undefined') return null
  const raw = window.localStorage.getItem(WALLET_SESSION_STORAGE_KEY)
  if (!raw) return null
  try {
    return JSON.parse(raw) as WalletAccessSession
  } catch {
    window.localStorage.removeItem(WALLET_SESSION_STORAGE_KEY)
    return null
  }
}

function setStoredWalletSession(session: WalletAccessSession | null) {
  if (typeof window === 'undefined') return
  if (session) {
    window.localStorage.setItem(WALLET_SESSION_STORAGE_KEY, JSON.stringify(session))
  } else {
    window.localStorage.removeItem(WALLET_SESSION_STORAGE_KEY)
  }
  window.dispatchEvent(new Event(ACCESS_SESSION_EVENT))
}

function buildWalletMessage(nonce: string) {
  return `Sign this message to authenticate with SolaceScan.\n\nNonce: ${nonce}`
}

async function fetchWalletJson<T>(path: string, init?: RequestInit): Promise<T> {
  const headers = new Headers(init?.headers || {})
  headers.set('Content-Type', 'application/json')
  const response = await fetch(`${getExplorerApiBase()}${path}`, {
    ...init,
    headers,
  })
  const payload = await response.json().catch(() => null)
  if (!response.ok) {
    throw new Error(payload?.error?.message || `HTTP ${response.status}`)
  }
  return payload as T
}

async function fetchJson<T>(path: string, init?: RequestInit, includeAuth = false): Promise<T> {
  const headers = new Headers(init?.headers || {})
  headers.set('Content-Type', 'application/json')
  if (includeAuth) {
    const token = getStoredAccessToken()
    if (token) {
      headers.set('Authorization', `Bearer ${token}`)
    }
  }
  const response = await fetch(`${getExplorerApiBase()}${path}`, {
    ...init,
    headers,
  })
  const payload = await response.json().catch(() => null)
  if (!response.ok) {
    throw new Error(payload?.error?.message || `HTTP ${response.status}`)
  }
  return payload as T
}

export const accessApi = {
  getStoredAccessToken,
  getStoredWalletSession,
  clearSession() {
    setStoredAccessToken('')
  },
  clearWalletSession() {
    setStoredWalletSession(null)
  },
  async register(email: string, username: string, password: string): Promise<AccessSession> {
    const response = await fetchJson<AccessSession>(`${ACCESS_API_PREFIX}/auth/register`, {
      method: 'POST',
      body: JSON.stringify({ email, username, password }),
    })
    setStoredAccessToken(response.token)
    return response
  },
  async login(email: string, password: string): Promise<AccessSession> {
    const response = await fetchJson<AccessSession>(`${ACCESS_API_PREFIX}/auth/login`, {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    })
    setStoredAccessToken(response.token)
    return response
  },
  async createWalletNonce(address: string): Promise<{ nonce: string; address: string }> {
    return fetchWalletJson<{ nonce: string; address: string }>(`${ACCESS_API_PREFIX}/auth/nonce`, {
      method: 'POST',
      body: JSON.stringify({ address }),
    })
  },
  async authenticateWallet(address: string, signature: string, nonce: string): Promise<WalletAccessSession> {
    const response = await fetchWalletJson<{
      token: string
      expires_at: string
      track: string
      permissions: string[]
    }>(`${ACCESS_API_PREFIX}/auth/wallet`, {
      method: 'POST',
      body: JSON.stringify({ address, signature, nonce }),
    })
    const session: WalletAccessSession = {
      token: response.token,
      expiresAt: response.expires_at,
      track: response.track,
      permissions: response.permissions || [],
      address,
    }
    setStoredWalletSession(session)
    return session
  },
  async connectWalletSession(): Promise<WalletAccessSession> {
    if (typeof window === 'undefined' || typeof window.ethereum === 'undefined') {
      throw new Error('No EOA wallet detected. Please open the explorer with a browser wallet installed.')
    }

    const accounts = (await window.ethereum.request({
      method: 'eth_requestAccounts',
    })) as string[]
    const address = accounts?.[0]
    if (!address) {
      throw new Error('Wallet connection was cancelled.')
    }

    const nonceResponse = await accessApi.createWalletNonce(address)
    const message = buildWalletMessage(nonceResponse.nonce)
    const signature = (await window.ethereum.request({
      method: 'personal_sign',
      params: [message, address],
    })) as string

    return accessApi.authenticateWallet(address, signature, nonceResponse.nonce)
  },
  async getMe(): Promise<{ user: AccessUser; subscriptions?: AccessSubscription[] }> {
    return fetchJson<{ user: AccessUser; subscriptions?: AccessSubscription[] }>(`${ACCESS_API_PREFIX}/access/me`, undefined, true)
  },
  async listProducts(): Promise<{ products: AccessProduct[]; note?: string }> {
    return fetchJson<{ products: AccessProduct[]; note?: string }>(`${ACCESS_API_PREFIX}/access/products`)
  },
  async listAPIKeys(): Promise<{ api_keys: AccessAPIKeyRecord[] }> {
    return fetchJson<{ api_keys: AccessAPIKeyRecord[] }>(`${ACCESS_API_PREFIX}/access/api-keys`, undefined, true)
  },
  async createAPIKey(request: CreateAccessAPIKeyRequest): Promise<{ api_key: string; record?: AccessAPIKeyRecord }> {
    return fetchJson<{ api_key: string; record?: AccessAPIKeyRecord }>(`${ACCESS_API_PREFIX}/access/api-keys`, {
      method: 'POST',
      body: JSON.stringify({
        name: request.name,
        tier: request.tier,
        product_slug: request.productSlug,
        expires_days: request.expiresDays,
        monthly_quota: request.monthlyQuota,
        scopes: request.scopes,
      }),
    }, true)
  },
  async revokeAPIKey(id: string): Promise<{ revoked: boolean; api_key_id: string }> {
    return fetchJson<{ revoked: boolean; api_key_id: string }>(`${ACCESS_API_PREFIX}/access/api-keys/${id}`, {
      method: 'POST',
    }, true)
  },
  async listSubscriptions(): Promise<{ subscriptions: AccessSubscription[] }> {
    return fetchJson<{ subscriptions: AccessSubscription[] }>(`${ACCESS_API_PREFIX}/access/subscriptions`, undefined, true)
  },
  async requestSubscription(productSlug: string, tier: string): Promise<{ subscription: AccessSubscription }> {
    return fetchJson<{ subscription: AccessSubscription }>(`${ACCESS_API_PREFIX}/access/subscriptions`, {
      method: 'POST',
      body: JSON.stringify({ product_slug: productSlug, tier }),
    }, true)
  },
  async getUsage(): Promise<{ usage: AccessUsageSummary[] }> {
    return fetchJson<{ usage: AccessUsageSummary[] }>(`${ACCESS_API_PREFIX}/access/usage`, undefined, true)
  },
  async listAudit(limit = 20): Promise<{ entries: AccessAuditEntry[] }> {
    return fetchJson<{ entries: AccessAuditEntry[] }>(`${ACCESS_API_PREFIX}/access/audit?limit=${encodeURIComponent(limit)}`, undefined, true)
  },
  async listAdminSubscriptions(status = 'pending'): Promise<{ subscriptions: AccessSubscription[] }> {
    const suffix = status ? `?status=${encodeURIComponent(status)}` : ''
    return fetchJson<{ subscriptions: AccessSubscription[] }>(`${ACCESS_API_PREFIX}/access/admin/subscriptions${suffix}`, undefined, true)
  },
  async listAdminAudit(limit = 50, productSlug = ''): Promise<{ entries: AccessAuditEntry[] }> {
    const params = new URLSearchParams()
    params.set('limit', String(limit))
    if (productSlug) params.set('product', productSlug)
    return fetchJson<{ entries: AccessAuditEntry[] }>(`${ACCESS_API_PREFIX}/access/admin/audit?${params.toString()}`, undefined, true)
  },
  async updateAdminSubscription(subscriptionId: string, status: string, notes = ''): Promise<{ subscription: AccessSubscription }> {
    return fetchJson<{ subscription: AccessSubscription }>(`${ACCESS_API_PREFIX}/access/admin/subscriptions`, {
      method: 'POST',
      body: JSON.stringify({
        subscription_id: subscriptionId,
        status,
        notes,
      }),
    }, true)
  },
}
