import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type, x-shoprenter-shop, x-shoprenter-token, x-shoprenter-api-version',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
}

/**
 * ShopRenter API Proxy
 *
 * This Edge Function acts as a transparent proxy to the ShopRenter API.
 * It solves the HTTP version compatibility issue where n8n uses HTTP/2+
 * but ShopRenter API requires HTTP/1.1 with Connection: close.
 *
 * Usage from n8n:
 * 1. Set X-ShopRenter-Token header with access token (from ShopRenter OAuth)
 *    - Use X-ShopRenter-Token instead of Authorization to avoid Supabase JWT validation
 * 2. Set X-ShopRenter-Shop header with shop name (e.g., "elektromosroller")
 * 3. (Optional) Set X-ShopRenter-API-Version header to specify API endpoint:
 *    - "api2" for api2.myshoprenter.hu (default)
 *    - "api" for api.myshoprenter.hu
 * 4. Make requests to: /shoprenter-proxy/api/{endpoint}
 *
 * All request methods (GET, POST, PUT, PATCH, DELETE) are supported.
 * Request and response bodies are proxied as-is.
 */

/**
 * Makes an HTTP/1.0 request using raw TCP/TLS connection
 * This is necessary because ShopRenter API requires HTTP/1.0
 * and Deno's fetch API uses HTTP/2 which causes connection issues
 */
async function makeHttp11Request(
  hostname: string,
  path: string,
  method: string,
  headers: Record<string, string>,
  body?: string
): Promise<{ status: number; statusText: string; headers: Record<string, string>; body: Uint8Array }> {
  // Open TLS connection
  const conn = await Deno.connectTls({
    hostname: hostname,
    port: 443,
  })

  try {
    // Build HTTP/1.0 request (ShopRenter requires HTTP/1.0)
    const requestLines: string[] = [
      `${method} ${path} HTTP/1.0`,
      `Host: ${hostname}`,
    ]

    // Add all headers
    for (const [key, value] of Object.entries(headers)) {
      requestLines.push(`${key}: ${value}`)
    }

    // Add Connection: close for HTTP/1.0
    requestLines.push('Connection: close')

    // Empty line to end headers
    requestLines.push('')
    requestLines.push('')

    // Build the complete request
    let request = requestLines.join('\r\n')

    // Add body if present (after the double CRLF)
    if (body) {
      request = request + body
    }

    console.log('[HTTP/1.0] Sending request:', request.split('\r\n').slice(0, 10).join('\n'))

    // Send request
    const encoder = new TextEncoder()
    await conn.write(encoder.encode(request))

    // Read response as binary data to preserve gzip/compressed content
    const chunks: Uint8Array[] = []
    const buffer = new Uint8Array(64 * 1024) // 64KB buffer per chunk
    let totalBytes = 0

    while (true) {
      try {
        const n = await conn.read(buffer)
        if (n === null) break

        // Copy the chunk to preserve it
        const chunk = new Uint8Array(n)
        chunk.set(buffer.subarray(0, n))
        chunks.push(chunk)
        totalBytes += n
      } catch (e) {
        // Handle UnexpectedEof during read - connection closed by server
        // This is expected when server sends Connection: close
        if (e instanceof Deno.errors.UnexpectedEof ||
            (e instanceof Error && e.name === 'UnexpectedEof')) {
          console.log('[HTTP/1.0] Connection closed by server (expected with Connection: close)')
          break
        }
        throw e
      }
    }

    console.log('[HTTP/1.0] Received response bytes:', totalBytes)

    // Concatenate all chunks into a single Uint8Array
    const responseData = new Uint8Array(totalBytes)
    let offset = 0
    for (const chunk of chunks) {
      responseData.set(chunk, offset)
      offset += chunk.length
    }

    // Parse HTTP response headers - we need to find the header/body separator
    // Headers are always ASCII text, so we can decode them safely
    const decoder = new TextDecoder()

    // Search for \r\n\r\n or \n\n in the binary data
    let headerEndIndex = -1
    let headerSeparatorLength = 0

    // Look for \r\n\r\n (most common)
    for (let i = 0; i < responseData.length - 3; i++) {
      if (responseData[i] === 13 && responseData[i + 1] === 10 &&
          responseData[i + 2] === 13 && responseData[i + 3] === 10) {
        headerEndIndex = i
        headerSeparatorLength = 4
        break
      }
    }

    // If not found, look for \n\n
    if (headerEndIndex === -1) {
      for (let i = 0; i < responseData.length - 1; i++) {
        if (responseData[i] === 10 && responseData[i + 1] === 10) {
          headerEndIndex = i
          headerSeparatorLength = 2
          break
        }
      }
    }

    if (headerEndIndex === -1) {
      const preview = decoder.decode(responseData.subarray(0, Math.min(200, responseData.length)))
      console.error('[HTTP/1.0] Could not find header separator. Response preview:', preview)
      throw new Error('Invalid HTTP response: no header/body separator found')
    }

    // Decode headers only (they're ASCII)
    const headerBytes = responseData.subarray(0, headerEndIndex)
    const headerSection = decoder.decode(headerBytes)

    // Body is everything after the separator (keep as binary)
    const bodyBytes = responseData.subarray(headerEndIndex + headerSeparatorLength)

    // Parse status line
    const lines = headerSection.split(/\r?\n/)
    const statusLine = lines[0]
    const statusMatch = statusLine.match(/HTTP\/1\.[01]\s+(\d+)\s+(.*)/)

    if (!statusMatch) {
      console.error('[HTTP/1.0] Invalid status line:', statusLine)
      throw new Error(`Invalid HTTP status line: ${statusLine}`)
    }

    const status = parseInt(statusMatch[1])
    const statusText = statusMatch[2]

    // Parse headers
    const responseHeaders: Record<string, string> = {}
    for (let i = 1; i < lines.length; i++) {
      const colonIndex = lines[i].indexOf(':')
      if (colonIndex > 0) {
        const key = lines[i].substring(0, colonIndex).trim()
        const value = lines[i].substring(colonIndex + 1).trim()
        responseHeaders[key] = value
      }
    }

    console.log('[HTTP/1.0] Response received:', status, statusText)
    console.log('[HTTP/1.0] Content-Encoding:', responseHeaders['Content-Encoding'] || 'none')
    console.log('[HTTP/1.0] Body size:', bodyBytes.length, 'bytes')

    return {
      status,
      statusText,
      headers: responseHeaders,
      body: bodyBytes,
    }
  } finally {
    try {
      conn.close()
    } catch (e) {
      // Gracefully handle connection closure errors
      // UnexpectedEof during close means server already closed the connection
      if (e instanceof Deno.errors.UnexpectedEof ||
          (e instanceof Error && e.name === 'UnexpectedEof')) {
        console.log('[HTTP/1.0] Connection already closed by server')
      } else {
        console.error('[HTTP/1.0] Error closing connection:', e)
      }
    }
  }
}

serve(async (req) => {
  // Handle CORS preflight
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  try {
    // Extract shop name from custom header
    const shopName = req.headers.get('X-ShopRenter-Shop')
    if (!shopName) {
      return new Response(
        JSON.stringify({
          error: 'Missing X-ShopRenter-Shop header',
          message: 'Please provide the shop name in the X-ShopRenter-Shop header (e.g., "elektromosroller")'
        }),
        {
          status: 400,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      )
    }

    // Extract ShopRenter token from custom header
    const shopRenterToken = req.headers.get('X-ShopRenter-Token')
    if (!shopRenterToken) {
      return new Response(
        JSON.stringify({
          error: 'Missing X-ShopRenter-Token header',
          message: 'Please provide the ShopRenter access token in the X-ShopRenter-Token header'
        }),
        {
          status: 400,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      )
    }

    // Extract API endpoint from URL path
    const url = new URL(req.url)

    console.log('[ShopRenter Proxy] Received URL:', req.url)
    console.log('[ShopRenter Proxy] URL pathname:', url.pathname)

    // For Supabase Edge Functions, the pathname is just the path after the function name
    // Example: If called as /functions/v1/shoprenter-proxy/api/customers
    // Then req.url pathname will be: /api/customers
    // For local testing, it might be: /shoprenter-proxy/api/customers

    let apiPath = url.pathname

    // If the path starts with /shoprenter-proxy/, remove that prefix
    if (apiPath.startsWith('/shoprenter-proxy/')) {
      apiPath = apiPath.substring('/shoprenter-proxy'.length)
    }

    // Validate that we have an API path
    if (!apiPath || apiPath === '/') {
      return new Response(
        JSON.stringify({
          error: 'Invalid API endpoint',
          message: 'Request path must include the API endpoint (e.g., /api/customers)',
          receivedUrl: req.url,
          receivedPath: url.pathname
        }),
        {
          status: 400,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      )
    }

    // Get API version from header (default to 'api2' for api2.myshoprenter.hu)
    const apiVersion = req.headers.get('X-ShopRenter-API-Version') || 'api2'

    // Validate API version
    if (apiVersion !== 'api' && apiVersion !== 'api2') {
      return new Response(
        JSON.stringify({
          error: 'Invalid X-ShopRenter-API-Version header',
          message: 'API version must be either "api" or "api2"',
          receivedValue: apiVersion
        }),
        {
          status: 400,
          headers: { ...corsHeaders, 'Content-Type': 'application/json' }
        }
      )
    }

    // Construct ShopRenter API URL components
    const hostname = `${shopName}.${apiVersion}.myshoprenter.hu`
    const fullPath = `${apiPath}${url.search}`

    console.log(`[ShopRenter Proxy] Using API version: ${apiVersion}`)
    console.log(`[ShopRenter Proxy] Forwarding ${req.method} request to: https://${hostname}${fullPath}`)

    // Prepare headers for ShopRenter API request
    const shopRenterHeaders: Record<string, string> = {}

    // Copy all headers from the original request except Host, Authorization, and custom headers
    for (const [key, value] of req.headers.entries()) {
      const lowerKey = key.toLowerCase()
      if (
        lowerKey !== 'host' &&
        lowerKey !== 'authorization' && // Skip Supabase auth
        !lowerKey.startsWith('x-shoprenter-') &&
        !lowerKey.startsWith('x-client-info')
      ) {
        shopRenterHeaders[key] = value
      }
    }

    // Set Authorization header with ShopRenter token
    shopRenterHeaders['Authorization'] = `Bearer ${shopRenterToken}`

    // Ensure required headers are present
    if (!shopRenterHeaders['Content-Type']) {
      shopRenterHeaders['Content-Type'] = 'application/json'
    }
    if (!shopRenterHeaders['Accept']) {
      shopRenterHeaders['Accept'] = 'application/json'
    }

    // Read request body if present
    let body: string | undefined = undefined
    if (req.method !== 'GET' && req.method !== 'HEAD') {
      const contentType = req.headers.get('Content-Type') || ''
      if (contentType.includes('application/json')) {
        try {
          body = await req.text()
          if (body) {
            shopRenterHeaders['Content-Length'] = body.length.toString()
          }
        } catch (e) {
          console.error('[ShopRenter Proxy] Error reading request body:', e)
        }
      }
    }

    console.log(`[ShopRenter Proxy] Making HTTP/1.0 request to: ${hostname}${fullPath}`)

    // Make HTTP/1.0 request using raw TCP/TLS connection
    const shopRenterResponse = await makeHttp11Request(
      hostname,
      fullPath,
      req.method,
      shopRenterHeaders,
      body
    )

    console.log(`[ShopRenter Proxy] ShopRenter API responded with status: ${shopRenterResponse.status}`)

    // Prepare response headers
    const responseHeaders = new Headers(corsHeaders)

    // Copy relevant headers from ShopRenter response
    for (const [key, value] of Object.entries(shopRenterResponse.headers)) {
      const lowerKey = key.toLowerCase()
      // Copy headers but skip transfer-encoding and connection as they're handled by the Edge Function
      if (lowerKey !== 'transfer-encoding' && lowerKey !== 'connection') {
        responseHeaders.set(key, value)
      }
    }

    // Return proxied response with binary body
    // The body is returned as Uint8Array to preserve gzip/compressed content
    return new Response(
      shopRenterResponse.body,
      {
        status: shopRenterResponse.status,
        statusText: shopRenterResponse.statusText,
        headers: responseHeaders
      }
    )

  } catch (error) {
    console.error('[ShopRenter Proxy] Error:', error)

    return new Response(
      JSON.stringify({
        error: 'Proxy error',
        message: error instanceof Error ? error.message : 'Unknown error occurred',
        details: error instanceof Error ? error.stack : undefined
      }),
      {
        status: 500,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
      }
    )
  }
})
