// src/lib/integrations/meta/MetaBaseClient.ts
// Base Client für Meta Graph API (Facebook + Instagram)

import type {
  MetaUser,
  FacebookPage,
  InstagramBusinessAccount,
  MetaPaginatedResponse,
  MetaErrorResponse,
} from '@/types/meta'
import { META_CONFIG } from './oauth'

// =============================================================================
// Error Types
// =============================================================================

export class MetaApiError extends Error {
  code: number
  subcode?: number
  type: string
  fbtraceId: string

  constructor(error: MetaErrorResponse['error']) {
    super(error.message)
    this.name = 'MetaApiError'
    this.code = error.code
    this.subcode = error.error_subcode
    this.type = error.type
    this.fbtraceId = error.fbtrace_id
  }

  isRateLimitError(): boolean {
    return this.code === 4 || this.code === 17 || this.code === 32
  }

  isAuthError(): boolean {
    return this.code === 190 || this.code === 102
  }

  isPermissionError(): boolean {
    return this.code === 10 || this.code === 200 || this.code === 230
  }
}

// =============================================================================
// Base Client
// =============================================================================

export class MetaBaseClient {
  protected accessToken: string
  protected apiVersion: string
  protected baseUrl: string

  constructor(accessToken: string) {
    this.accessToken = accessToken
    this.apiVersion = META_CONFIG.apiVersion
    this.baseUrl = META_CONFIG.baseUrl
  }

  // ===========================================================================
  // Core Request Method
  // ===========================================================================

  protected async request<T>(
    endpoint: string,
    options: {
      method?: 'GET' | 'POST' | 'DELETE'
      params?: Record<string, string | number | boolean | undefined>
      body?: Record<string, unknown>
    } = {}
  ): Promise<T> {
    const { method = 'GET', params = {}, body } = options

    // Build URL with params
    const url = new URL(`${this.baseUrl}/${this.apiVersion}/${endpoint}`)

    // Add access token
    url.searchParams.set('access_token', this.accessToken)

    // Add other params
    for (const [key, value] of Object.entries(params)) {
      if (value !== undefined) {
        url.searchParams.set(key, String(value))
      }
    }

    // Build request options
    const fetchOptions: RequestInit = {
      method,
      headers: {
        'Content-Type': 'application/json',
      },
    }

    if (body && method !== 'GET') {
      fetchOptions.body = JSON.stringify(body)
    }

    // Execute request
    const response = await fetch(url.toString(), fetchOptions)
    const data = await response.json()

    // Handle errors
    if (!response.ok || data.error) {
      const error = data.error || {
        message: 'Unknown error',
        type: 'UnknownError',
        code: response.status,
        fbtrace_id: 'unknown',
      }
      throw new MetaApiError(error)
    }

    return data as T
  }

  // ===========================================================================
  // Paginated Request Helper
  // ===========================================================================

  protected async requestPaginated<T>(
    endpoint: string,
    options: {
      params?: Record<string, string | number | boolean | undefined>
      limit?: number
      maxPages?: number
    } = {}
  ): Promise<T[]> {
    const { params = {}, limit = 100, maxPages = 10 } = options
    const allItems: T[] = []
    let pageCount = 0
    let nextUrl: string | undefined

    // Initial request
    const initialParams = { ...params, limit }
    const response = await this.request<MetaPaginatedResponse<T>>(endpoint, {
      params: initialParams,
    })

    allItems.push(...response.data)
    nextUrl = response.paging?.next
    pageCount++

    // Fetch additional pages
    while (nextUrl && pageCount < maxPages) {
      const nextResponse = await fetch(nextUrl)
      const nextData: MetaPaginatedResponse<T> = await nextResponse.json()

      if ('error' in (nextData as unknown as MetaErrorResponse)) {
        throw new MetaApiError((nextData as unknown as MetaErrorResponse).error)
      }

      allItems.push(...nextData.data)
      nextUrl = nextData.paging?.next
      pageCount++

      // Rate limiting
      await this.sleep(100)
    }

    return allItems
  }

  // ===========================================================================
  // User & Account Methods
  // ===========================================================================

  /**
   * Hole Informationen über den authentifizierten User
   */
  async getMe(): Promise<MetaUser> {
    return this.request<MetaUser>('me', {
      params: {
        fields: 'id,name,email',
      },
    })
  }

  /**
   * Hole alle Facebook Pages des Users
   */
  async getPages(): Promise<FacebookPage[]> {
    const response = await this.request<MetaPaginatedResponse<FacebookPage>>(
      'me/accounts',
      {
        params: {
          fields:
            'id,name,access_token,category,category_list,tasks,instagram_business_account',
        },
      }
    )
    return response.data
  }

  /**
   * Hole Instagram Business Account für eine Page
   */
  async getInstagramAccount(pageId: string): Promise<InstagramBusinessAccount | null> {
    try {
      const response = await this.request<{
        instagram_business_account?: InstagramBusinessAccount
      }>(`${pageId}`, {
        params: {
          fields:
            'instagram_business_account{id,username,name,profile_picture_url,followers_count,follows_count,media_count,biography,website}',
        },
      })

      return response.instagram_business_account || null
    } catch (error) {
      if (error instanceof MetaApiError && error.isPermissionError()) {
        return null
      }
      throw error
    }
  }

  /**
   * Hole alle Instagram Business Accounts des Users (über alle Pages)
   */
  async getAllInstagramAccounts(): Promise<
    Array<{
      page: FacebookPage
      instagram: InstagramBusinessAccount
    }>
  > {
    const pages = await this.getPages()
    const results: Array<{
      page: FacebookPage
      instagram: InstagramBusinessAccount
    }> = []

    for (const page of pages) {
      if (page.instagram_business_account?.id) {
        const instagram = await this.getInstagramAccount(page.id)
        if (instagram) {
          results.push({ page, instagram })
        }
      }

      // Rate limiting
      await this.sleep(100)
    }

    return results
  }

  // ===========================================================================
  // Token Management
  // ===========================================================================

  /**
   * Aktualisiert den Access Token
   */
  setAccessToken(token: string): void {
    this.accessToken = token
  }

  /**
   * Gibt den aktuellen Access Token zurück
   */
  getAccessToken(): string {
    return this.accessToken
  }

  // ===========================================================================
  // Utility Methods
  // ===========================================================================

  protected sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms))
  }

  /**
   * Rate Limiting Helper - wartet bei Rate Limit Errors
   */
  protected async withRetry<T>(
    fn: () => Promise<T>,
    maxRetries: number = 3,
    initialDelay: number = 1000
  ): Promise<T> {
    let lastError: Error | undefined
    let delay = initialDelay

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        return await fn()
      } catch (error) {
        lastError = error as Error

        if (error instanceof MetaApiError && error.isRateLimitError()) {
          console.warn(
            `[MetaClient] Rate limit hit, waiting ${delay}ms before retry ${attempt + 1}/${maxRetries}`
          )
          await this.sleep(delay)
          delay *= 2 // Exponential backoff
        } else {
          throw error
        }
      }
    }

    throw lastError
  }
}

export default MetaBaseClient
