// src/lib/integrations/meta/oauth.ts
// Meta OAuth 2.0 Implementation für Facebook und Instagram

import type {
  MetaTokens,
  MetaLongLivedToken,
  MetaTokenDebugInfo,
  MetaOAuthState,
} from '@/types/meta'

// =============================================================================
// Configuration
// =============================================================================

const META_API_VERSION = 'v19.0'
const META_BASE_URL = 'https://graph.facebook.com'
const META_AUTH_URL = 'https://www.facebook.com'

// Scopes für Facebook + Instagram
const META_SCOPES = [
  // Facebook Page Management
  'pages_show_list',
  'pages_read_engagement',
  'pages_manage_posts',
  'pages_read_user_content',
  'pages_manage_engagement',
  'pages_messaging',

  // Instagram
  'instagram_basic',
  'instagram_manage_comments',
  'instagram_manage_messages',
  'instagram_content_publish',

  // Business
  'business_management',

  // User Info
  'public_profile',
  'email',
]

// =============================================================================
// Helper Functions
// =============================================================================

function getConfig() {
  const appId = process.env.META_APP_ID
  const appSecret = process.env.META_APP_SECRET
  const redirectUri = process.env.META_REDIRECT_URI

  if (!appId || !appSecret || !redirectUri) {
    throw new Error(
      'Missing Meta OAuth configuration. Please set META_APP_ID, META_APP_SECRET, and META_REDIRECT_URI environment variables.'
    )
  }

  return { appId, appSecret, redirectUri }
}

function encodeState(state: MetaOAuthState): string {
  return Buffer.from(JSON.stringify(state)).toString('base64url')
}

function decodeState(encoded: string): MetaOAuthState {
  try {
    return JSON.parse(Buffer.from(encoded, 'base64url').toString('utf-8'))
  } catch {
    throw new Error('Invalid OAuth state')
  }
}

// =============================================================================
// OAuth Functions
// =============================================================================

/**
 * Generiert die Authorization URL für den Meta OAuth Flow
 */
export function getAuthUrl(options: {
  returnUrl?: string
  accountType?: 'facebook' | 'instagram' | 'both'
  userId?: number
  socialAccountId?: number
}): string {
  const config = getConfig()
  const { returnUrl = '/admin', accountType = 'both', userId, socialAccountId } = options

  const state: MetaOAuthState = {
    returnUrl,
    accountType,
    userId,
    socialAccountId,
    timestamp: Date.now(),
  }

  const params = new URLSearchParams({
    client_id: config.appId,
    redirect_uri: config.redirectUri,
    scope: META_SCOPES.join(','),
    response_type: 'code',
    state: encodeState(state),
  })

  return `${META_AUTH_URL}/${META_API_VERSION}/dialog/oauth?${params.toString()}`
}

/**
 * Tauscht den Authorization Code gegen Access Token
 */
export async function exchangeCodeForTokens(code: string): Promise<MetaTokens> {
  const config = getConfig()

  const params = new URLSearchParams({
    client_id: config.appId,
    client_secret: config.appSecret,
    redirect_uri: config.redirectUri,
    code,
  })

  const response = await fetch(
    `${META_BASE_URL}/${META_API_VERSION}/oauth/access_token?${params.toString()}`
  )

  if (!response.ok) {
    const error = await response.json()
    console.error('[Meta OAuth] Token exchange failed:', error)
    throw new Error(error.error?.message || 'Failed to exchange code for tokens')
  }

  return response.json()
}

/**
 * Wandelt Short-lived Token in Long-lived Token um (60 Tage gültig)
 */
export async function getLongLivedToken(
  shortLivedToken: string
): Promise<MetaLongLivedToken> {
  const config = getConfig()

  const params = new URLSearchParams({
    grant_type: 'fb_exchange_token',
    client_id: config.appId,
    client_secret: config.appSecret,
    fb_exchange_token: shortLivedToken,
  })

  const response = await fetch(
    `${META_BASE_URL}/${META_API_VERSION}/oauth/access_token?${params.toString()}`
  )

  if (!response.ok) {
    const error = await response.json()
    console.error('[Meta OAuth] Long-lived token exchange failed:', error)
    throw new Error(error.error?.message || 'Failed to get long-lived token')
  }

  return response.json()
}

/**
 * Refresht einen Long-lived Token (vor Ablauf)
 * Nur möglich wenn Token noch gültig ist!
 */
export async function refreshLongLivedToken(
  token: string
): Promise<MetaLongLivedToken> {
  const config = getConfig()

  const params = new URLSearchParams({
    grant_type: 'fb_exchange_token',
    client_id: config.appId,
    client_secret: config.appSecret,
    fb_exchange_token: token,
  })

  const response = await fetch(
    `${META_BASE_URL}/${META_API_VERSION}/oauth/access_token?${params.toString()}`
  )

  if (!response.ok) {
    const error = await response.json()
    console.error('[Meta OAuth] Token refresh failed:', error)
    throw new Error(error.error?.message || 'Failed to refresh token')
  }

  return response.json()
}

/**
 * Validiert einen Token und gibt Debug-Informationen zurück
 */
export async function debugToken(token: string): Promise<MetaTokenDebugInfo> {
  const config = getConfig()

  const params = new URLSearchParams({
    input_token: token,
    access_token: `${config.appId}|${config.appSecret}`,
  })

  const response = await fetch(
    `${META_BASE_URL}/${META_API_VERSION}/debug_token?${params.toString()}`
  )

  if (!response.ok) {
    const error = await response.json()
    throw new Error(error.error?.message || 'Failed to debug token')
  }

  const result = await response.json()
  return result.data
}

/**
 * Prüft ob ein Token noch gültig ist
 */
export async function isTokenValid(token: string): Promise<boolean> {
  try {
    const debug = await debugToken(token)
    return debug.is_valid && debug.expires_at > Math.floor(Date.now() / 1000)
  } catch {
    return false
  }
}

/**
 * Berechnet das Ablaufdatum eines Tokens
 */
export function calculateTokenExpiry(expiresIn: number): Date {
  return new Date(Date.now() + expiresIn * 1000)
}

/**
 * Prüft ob Token bald abläuft (innerhalb der nächsten X Tage)
 */
export function isTokenExpiringSoon(expiresAt: Date, daysThreshold: number = 7): boolean {
  const threshold = new Date()
  threshold.setDate(threshold.getDate() + daysThreshold)
  return expiresAt <= threshold
}

// =============================================================================
// State Management
// =============================================================================

export { encodeState, decodeState }

// =============================================================================
// Constants Export
// =============================================================================

export const META_CONFIG = {
  apiVersion: META_API_VERSION,
  baseUrl: META_BASE_URL,
  authUrl: META_AUTH_URL,
  scopes: META_SCOPES,
  tokenValidityDays: 60,
  refreshThresholdDays: 7,
}
