import { string } from 'alga-js'
import refreshTokenHelper from "../../utils/refreshTokenHelper"
import forceLogoutHelper from "../../utils/forceLogoutHelper"
import errorHandlingHelper from "../../utils/errorHandlingHelper"
import fetchHelper from "../../utils/fetchHelper"

// Amazon SP-API endpoints
const LWA_TOKEN_URL = 'https://api.amazon.com/auth/o2/token'
const SP_API_BASE_URL = 'https://sellingpartnerapi-eu.amazon.com'

// EU Marketplace IDs
const MARKETPLACE_IDS: Record<string, string> = {
  'DE': 'A1PA6795UKMFR9',
  'FR': 'A13V1IB3VIYZZH',
  'IT': 'APJ6JRA9NG5V4',
  'ES': 'A1RKKUPIHCS9HS',
  'NL': 'A1805IZSGTT6HS',
  'BE': 'AMEN7PMS3EDWL',
  'UK': 'A1F83G8C2ARO7P',
  'PL': 'A1C3SOZRARQ6R3',
  'SE': 'A2NODRKZP88ZB9',
  'AT': 'A2CVHYRTWLQO9T'
}

// Log collector for debugging
const createLogger = () => {
  const logs: string[] = []
  return {
    log: (message: string) => {
      const timestamp = new Date().toISOString()
      logs.push(`[${timestamp}] ${message}`)
    },
    getLogs: () => logs
  }
}

// Get LWA access token using refresh token
const getAmazonAccessToken = async (clientId: string, clientSecret: string, refreshToken: string, logger?: ReturnType<typeof createLogger>): Promise<string> => {
  logger?.log('Requesting LWA access token...')

  const response = await fetch(LWA_TOKEN_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
      client_id: clientId,
      client_secret: clientSecret
    })
  })

  if (!response.ok) {
    const errorText = await response.text()
    logger?.log(`LWA token error (${response.status}): ${errorText}`)
    throw new Error(`Failed to get Amazon access token (${response.status}): ${errorText}`)
  }

  const data: any = await response.json()
  logger?.log('LWA access token obtained successfully')
  return data.access_token
}

// Create feed document (get upload URL)
const createFeedDocument = async (accessToken: string, logger?: ReturnType<typeof createLogger>): Promise<{ feedDocumentId: string, url: string }> => {
  logger?.log('Creating feed document...')

  const response = await fetch(`${SP_API_BASE_URL}/feeds/2021-06-30/documents`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-amz-access-token': accessToken
    },
    body: JSON.stringify({ contentType: 'application/pdf' })
  })

  if (!response.ok) {
    const errorText = await response.text()
    logger?.log(`createFeedDocument error (${response.status}): ${errorText}`)
    throw new Error(`Failed to create feed document (${response.status}): ${errorText}`)
  }

  const data: any = await response.json()
  logger?.log(`Feed document created: ${data.feedDocumentId}`)
  return { feedDocumentId: data.feedDocumentId, url: data.url }
}

// Upload PDF to the pre-signed URL
const uploadPdfToAmazon = async (url: string, pdfBuffer: Buffer, logger?: ReturnType<typeof createLogger>): Promise<void> => {
  logger?.log(`Uploading PDF (${pdfBuffer.length} bytes) to pre-signed URL...`)

  const response = await fetch(url, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/pdf' },
    body: pdfBuffer
  })

  if (!response.ok) {
    const errorText = await response.text()
    logger?.log(`PDF upload error (${response.status}): ${errorText}`)
    throw new Error(`Failed to upload PDF to Amazon (${response.status}): ${errorText}`)
  }

  logger?.log('PDF uploaded successfully')
}

// Create the feed with UPLOAD_VAT_INVOICE type
const createFeed = async (
  accessToken: string,
  feedDocumentId: string,
  amazonOrderId: string,
  invoiceNumber: string,
  documentType: string,
  marketplaceId: string,
  logger?: ReturnType<typeof createLogger>
): Promise<any> => {
  logger?.log(`Creating feed: orderID=${amazonOrderId}, invoice=${invoiceNumber}, type=${documentType}, marketplace=${marketplaceId}`)

  const feedOptions: Record<string, string> = {
    'metadata:orderid': amazonOrderId,
    'metadata:invoicenumber': invoiceNumber,
    'metadata:documenttype': documentType
  }

  const response = await fetch(`${SP_API_BASE_URL}/feeds/2021-06-30/feeds`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-amz-access-token': accessToken
    },
    body: JSON.stringify({
      feedType: 'UPLOAD_VAT_INVOICE',
      marketplaceIds: [marketplaceId],
      inputFeedDocumentId: feedDocumentId,
      feedOptions
    })
  })

  if (!response.ok) {
    const errorText = await response.text()
    logger?.log(`createFeed error (${response.status}): ${errorText}`)
    throw new Error(`Failed to create feed (${response.status}): ${errorText}`)
  }

  const data: any = await response.json()
  logger?.log(`Feed created: ${data.feedId}`)
  return data
}

const handleFunc = async (event: any, authToken: any = null) => {
  const logger = createLogger()
  let data: any = {}
  const token = authToken ?? await getTokenHelper(event)
  const body = await readBody(event)
  const invoiceIds: number[] = body.ids || []
  const marketplaceCountry: string = body.marketplaceCountry || 'DE'

  logger.log(`Starting Amazon invoice upload for IDs: ${JSON.stringify(invoiceIds)}`)

  if (!invoiceIds.length) {
    return { status: 400, message: 'No invoice IDs provided', logs: logger.getLogs() }
  }

  const marketplaceId = MARKETPLACE_IDS[marketplaceCountry.toUpperCase()] || MARKETPLACE_IDS['DE']
  logger.log(`Using marketplace: ${marketplaceCountry} (${marketplaceId})`)

  const errors: string[] = []
  const skipped: string[] = []
  let uploadedCount = 0

  // Cache for order source credentials (to avoid re-fetching for same order source)
  const orderSourceCache: Record<number, any> = {}
  // Cache for LWA access tokens per order source
  const accessTokenCache: Record<number, string> = {}

  for (const invoiceId of invoiceIds) {
    try {
      logger.log(`--- Processing invoice ID: ${invoiceId} ---`)

      // Fetch invoice with order details
      const invoice: any = await event.context.fetch(
        `models/c_invoice/${invoiceId}?$expand=C_Order_ID($select=C_Order_ID,amazon_order_id,C_OrderSource_ID,DocumentNo)`,
        'GET',
        token,
        null
      )

      if (!invoice) {
        const msg = `Invoice ${invoiceId} not found`
        logger.log(msg)
        errors.push(msg)
        continue
      }

      logger.log(`Invoice found: ${invoice.DocumentNo} (DocBaseType: ${invoice.DocBaseType?.id || invoice.DocBaseType})`)

      // Check if already uploaded
      if (invoice.isUploadToAmazon === true || invoice.isUploadToAmazon === 'Y') {
        const msg = `Invoice ${invoice.DocumentNo} already uploaded to Amazon - skipping`
        logger.log(msg)
        skipped.push(msg)
        continue
      }

      // Check if invoice has a linked order with an Amazon order ID
      const amazonOrderId = invoice.C_Order_ID?.amazon_order_id
      if (!amazonOrderId) {
        const msg = `Invoice ${invoice.DocumentNo}: No Amazon order ID found on linked order`
        logger.log(msg)
        errors.push(msg)
        continue
      }

      logger.log(`Amazon Order ID: ${amazonOrderId}`)

      // Fetch order source credentials
      const orderSourceId = invoice.C_Order_ID?.C_OrderSource_ID?.id
      if (!orderSourceId) {
        const msg = `Invoice ${invoice.DocumentNo}: No order source found on linked order`
        logger.log(msg)
        errors.push(msg)
        continue
      }

      let orderSource = orderSourceCache[orderSourceId]
      if (!orderSource) {
        logger.log(`Fetching order source: ${orderSourceId}`)
        orderSource = await event.context.fetch(
          `models/c_ordersource/${orderSourceId}`,
          'GET',
          token,
          null
        )
        orderSourceCache[orderSourceId] = orderSource
      }

      if (!orderSource?.marketplace_key || !orderSource?.marketplace_secret || !orderSource?.marketplace_token) {
        const msg = `Invoice ${invoice.DocumentNo}: Order source missing Amazon SP-API credentials (marketplace_key, marketplace_secret, marketplace_token)`
        logger.log(msg)
        errors.push(msg)
        continue
      }

      // Get or cache LWA access token
      let accessToken = accessTokenCache[orderSourceId]
      if (!accessToken) {
        accessToken = await getAmazonAccessToken(
          orderSource.marketplace_key,
          orderSource.marketplace_secret,
          orderSource.marketplace_token,
          logger
        )
        accessTokenCache[orderSourceId] = accessToken
      }

      // Fetch invoice PDF from iDempiere
      logger.log(`Fetching PDF for invoice ${invoiceId}`)
      const pdfResponse: any = await event.context.fetch(
        `models/c_invoice/${invoiceId}/print?$report_type=PDF`,
        'GET',
        token,
        null
      )

      if (!pdfResponse?.reportFile) {
        const msg = `Invoice ${invoice.DocumentNo}: Could not generate PDF (reportFile missing)`
        logger.log(msg)
        errors.push(msg)
        continue
      }

      const pdfBuffer = Buffer.from(pdfResponse.reportFile, 'base64')
      logger.log(`PDF generated: ${pdfBuffer.length} bytes`)

      // Determine document type
      const docBaseType = invoice.DocBaseType?.id || invoice.DocBaseType?.identifier || invoice.DocBaseType || ''
      const documentType = (docBaseType === 'ARC') ? 'CreditNote' : 'Invoice'
      logger.log(`Document type: ${documentType} (DocBaseType: ${docBaseType})`)

      // Step 1: Create feed document
      const { feedDocumentId, url } = await createFeedDocument(accessToken, logger)

      // Step 2: Upload PDF
      await uploadPdfToAmazon(url, pdfBuffer, logger)

      // Step 3: Create feed
      const feedResult = await createFeed(
        accessToken,
        feedDocumentId,
        amazonOrderId,
        invoice.DocumentNo,
        documentType,
        marketplaceId,
        logger
      )

      // Step 4: Mark invoice as uploaded in iDempiere
      try {
        const uploadDate = new Date().toISOString()
        logger.log(`Marking invoice ${invoiceId} as uploaded to Amazon`)
        await event.context.fetch(
          `models/c_invoice/${invoiceId}`,
          'PUT',
          token,
          {
            isUploadToAmazon: true,
            UploadDateAmazon: uploadDate
          }
        )
        logger.log(`Successfully marked invoice as uploaded`)
      } catch (markErr: any) {
        logger.log(`Could not mark invoice as uploaded: ${markErr.message}`)
      }

      logger.log(`SUCCESS: Invoice ${invoice.DocumentNo} uploaded to Amazon (Feed ID: ${feedResult.feedId})`)
      uploadedCount++

      // Rate limiting: Amazon allows 1 invoice per 3 seconds
      await new Promise(resolve => setTimeout(resolve, 3000))

    } catch (err: any) {
      const msg = `Error processing invoice ${invoiceId}: ${err.message || 'Unknown error'}`
      logger.log(msg)
      errors.push(msg)
    }
  }

  logger.log(`=== SUMMARY: Uploaded ${uploadedCount} invoice(s), ${skipped.length} skipped, ${errors.length} error(s) ===`)

  // Output logs for debugging
  console.log('\n========== AMAZON INVOICE UPLOAD LOGS ==========')
  logger.getLogs().forEach(log => console.log(log))
  console.log('=================================================\n')

  if (uploadedCount > 0) {
    data = {
      status: 200,
      message: `Successfully uploaded ${uploadedCount} invoice(s) to Amazon${skipped.length > 0 ? `, ${skipped.length} skipped (already uploaded)` : ''}`,
      uploadedCount,
      skippedCount: skipped.length,
      uploadDate: new Date().toISOString(),
      errors: errors.length > 0 ? errors : undefined,
      skipped: skipped.length > 0 ? skipped : undefined,
      logs: logger.getLogs()
    }
  } else if (skipped.length > 0 && errors.length === 0) {
    data = {
      status: 200,
      message: `All ${skipped.length} invoice(s) were already uploaded to Amazon`,
      uploadedCount: 0,
      skippedCount: skipped.length,
      skipped,
      logs: logger.getLogs()
    }
  } else {
    data = {
      status: 400,
      message: errors.length > 0 ? errors.join('; ') : 'No invoices were uploaded to Amazon',
      errors,
      skipped: skipped.length > 0 ? skipped : undefined,
      logs: logger.getLogs()
    }
  }

  return data
}

export default defineEventHandler(async (event) => {
  let data: any = {}

  try {
    data = await handleFunc(event)
  } catch (err: any) {
    try {
      let authToken: any = await refreshTokenHelper(event)
      data = await handleFunc(event, authToken)
    } catch (error) {
      data = errorHandlingHelper(err?.data ?? err, error?.data ?? error)
      forceLogoutHelper(event, data)
    }
  }

  return data
})
