import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

export interface ShopRenterTokens {
  access_token: string
  refresh_token?: string
  token_type: string
  expires_at: string
  shopname: string
}

/**
 * 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 authentication issues
 */
async function makeHttp10Request(
  hostname: string,
  path: string,
  method: string,
  headers: Record<string, string>,
  body?: string
): Promise<{ status: number; statusText: string; headers: Record<string, string>; body: string }> {
  // 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 to:', hostname + path)

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

    // Read response as binary data
    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
    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
    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)

    // Decode body (handle gzip if present)
    let bodyText: string
    const contentEncoding = responseHeaders['Content-Encoding'] || responseHeaders['content-encoding']

    if (contentEncoding === 'gzip') {
      // Decompress gzip
      try {
        const decompressed = new DecompressionStream('gzip')
        const writer = decompressed.writable.getWriter()
        await writer.write(bodyBytes)
        await writer.close()

        const reader = decompressed.readable.getReader()
        const decompressedChunks: Uint8Array[] = []
        while (true) {
          const { done, value } = await reader.read()
          if (done) break
          decompressedChunks.push(value)
        }

        const decompressedData = new Uint8Array(
          decompressedChunks.reduce((acc, chunk) => acc + chunk.length, 0)
        )
        let pos = 0
        for (const chunk of decompressedChunks) {
          decompressedData.set(chunk, pos)
          pos += chunk.length
        }

        bodyText = decoder.decode(decompressedData)
      } catch (e) {
        console.error('[HTTP/1.0] Error decompressing gzip:', e)
        bodyText = decoder.decode(bodyBytes)
      }
    } else {
      bodyText = decoder.decode(bodyBytes)
    }

    return {
      status,
      statusText,
      headers: responseHeaders,
      body: bodyText,
    }
  } finally {
    try {
      conn.close()
    } catch (e) {
      // Gracefully handle connection closure errors
      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)
      }
    }
  }
}

export interface ShopRenterProduct {
  id: string
  name: string
  sku?: string
  price: string
  currency: string
  description?: string
  stock?: number
  active: boolean
}

export interface ShopRenterCustomer {
  id: string
  firstname: string
  lastname: string
  email: string
  phone?: string
  created_at: string
}

export interface ShopRenterOrder {
  id: string
  order_number?: string
  number?: string
  customer_id?: string
  customer?: {
    firstname?: string
    lastname?: string
    email?: string
    phone?: string
  }
  customer_name?: string
  customer_email?: string
  customer_phone?: string
  status: string
  total: string
  currency?: string
  created_at?: string
  date_created?: string
  items?: any[]
  line_items?: any[]
  billing_address?: {
    phone?: string
    [key: string]: any
  }
  shipping_address?: {
    phone?: string
    [key: string]: any
  }
}

// Get valid access token (with automatic refresh or client_credentials)
export async function getValidAccessToken(storeId: string): Promise<string> {
  const supabaseUrl = Deno.env.get('SUPABASE_URL')!
  const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  const supabase = createClient(supabaseUrl, supabaseServiceKey)

  // Fetch store data
  const { data: store, error: storeError } = await supabase
    .from('stores')
    .select('store_name, api_key, api_secret, access_token, refresh_token, alt_data, token_expires_at')
    .eq('id', storeId)
    .eq('platform_name', 'shoprenter')
    .single()

  if (storeError || !store) {
    throw new Error('ShopRenter store not found')
  }

  // Get client credentials - prioritize database, fallback to environment variables
  let clientId = store.api_key
  let clientSecret = store.api_secret

  // If api_key looks like an access_token (JWT format), it was corrupted - get from alt_data
  const isAccessToken = store.api_key && store.api_key.includes('.')

  if (isAccessToken) {
    console.log('[ShopRenter] WARNING: api_key appears to be an access_token (corrupted), retrieving credentials from alt_data')
    clientId = store.alt_data?.client_id
    clientSecret = store.alt_data?.client_secret
  }

  // If api_key/api_secret are NULL/empty (manual testing), try alt_data backup
  if (!clientId || !clientSecret) {
    clientId = store.alt_data?.client_id
    clientSecret = store.alt_data?.client_secret
  }

  // Final fallback: use global credentials from environment (for testing or when store has NULL credentials)
  if (!clientId || !clientSecret) {
    console.log('[ShopRenter] No client credentials in database, using global credentials from environment')
    clientId = Deno.env.get('SHOPRENTER_APP_CLIENT_ID')
    clientSecret = Deno.env.get('SHOPRENTER_APP_CLIENT_SECRET')
  }

  // Validate client credentials are available
  if (!clientId || !clientSecret) {
    throw new Error('ShopRenter client credentials not found in database or environment. Please reconnect the store or configure SHOPRENTER_APP_CLIENT_ID and SHOPRENTER_APP_CLIENT_SECRET.')
  }

  // If we have client credentials, use client_credentials flow to get a fresh token
  if (clientId && clientSecret) {
    console.log('[ShopRenter] Using client_credentials flow to obtain access token')

    try {
      const tokenData = await getTokenWithClientCredentials(store.store_name, clientId, clientSecret)

      const expiresAt = new Date(Date.now() + (tokenData.expires_in * 1000)).toISOString()

      // Update store with the new access token
      // IMPORTANT: Only update access_token, refresh_token, and token_expires_at
      // NEVER update api_key or api_secret (preserve them for manual testing or per-store credentials)
      // Always backup client credentials to alt_data for recovery
      await supabase
        .from('stores')
        .update({
          access_token: tokenData.access_token,
          refresh_token: tokenData.refresh_token || null,
          token_expires_at: expiresAt,
          alt_data: {
            ...(store.alt_data || {}),
            client_id: clientId,
            client_secret: clientSecret,
            last_token_refresh: new Date().toISOString()
          }
        })
        .eq('id', storeId)

      console.log('[ShopRenter] Access token obtained and stored successfully, expires at:', expiresAt)
      return tokenData.access_token
    } catch (error) {
      console.error('[ShopRenter] Failed to get token via client_credentials:', error)
      // Continue to try other methods below
    }
  }

  // Check if we have an existing access token
  if (store.access_token) {
    const expiresAt = store.token_expires_at
    if (expiresAt) {
      const expiryTime = new Date(expiresAt).getTime()
      const now = Date.now()
      const bufferTime = 5 * 60 * 1000 // 5 minutes

      // Token is still valid
      if (expiryTime - now > bufferTime) {
        console.log('[ShopRenter] Using existing valid access_token, expires at:', expiresAt)
        return store.access_token
      }

      // Token needs refresh
      console.log('[ShopRenter] Token expired or expiring soon, refreshing...')
      if (store.refresh_token && clientId && clientSecret) {
        try {
          const newTokenData = await refreshAccessToken(store.store_name, store.refresh_token, clientId, clientSecret)

          const newExpiresAt = new Date(Date.now() + (newTokenData.expires_in * 1000)).toISOString()

          // Update store with new tokens
          // IMPORTANT: Only update access_token, refresh_token, and token_expires_at
          // NEVER update api_key or api_secret (preserve them)
          // Always backup client credentials to alt_data for recovery
          await supabase
            .from('stores')
            .update({
              access_token: newTokenData.access_token,
              refresh_token: newTokenData.refresh_token || store.refresh_token,
              token_expires_at: newExpiresAt,
              alt_data: {
                ...(store.alt_data || {}),
                client_id: clientId,
                client_secret: clientSecret,
                last_token_refresh: new Date().toISOString()
              }
            })
            .eq('id', storeId)

          console.log('[ShopRenter] Token refreshed successfully, expires at:', newExpiresAt)
          return newTokenData.access_token
        } catch (refreshError) {
          console.error('[ShopRenter] Token refresh failed:', refreshError)

          // If refresh fails and we have client credentials, try client_credentials flow as fallback
          if (clientId && clientSecret) {
            console.log('[ShopRenter] Attempting fallback to client_credentials flow')
            const tokenData = await getTokenWithClientCredentials(
              store.store_name,
              clientId,
              clientSecret
            )

            const expiresAt = new Date(Date.now() + (tokenData.expires_in * 1000)).toISOString()

            // Update store with new tokens
            // IMPORTANT: Only update access_token, refresh_token, and token_expires_at
            // NEVER update api_key or api_secret (preserve them)
            // Always backup client credentials to alt_data for recovery
            await supabase
              .from('stores')
              .update({
                access_token: tokenData.access_token,
                refresh_token: tokenData.refresh_token || null,
                token_expires_at: expiresAt,
                alt_data: {
                  ...(store.alt_data || {}),
                  client_id: clientId,
                  client_secret: clientSecret,
                  last_token_refresh: new Date().toISOString()
                }
              })
              .eq('id', storeId)

            console.log('[ShopRenter] Access token obtained via client_credentials fallback, expires at:', expiresAt)
            return tokenData.access_token
          }

          // No fallback available, re-throw the error
          throw refreshError
        }
      }
    }

    // No expiration info, just return the token
    console.log('[ShopRenter] No expiration info found, using access_token as-is')
    return store.access_token
  }

  throw new Error('No access token found for ShopRenter store')
}

// Get access token using client_credentials grant
async function getTokenWithClientCredentials(shopname: string, clientId: string, clientSecret: string) {
  const hostname = 'oauth.app.shoprenter.net'
  const path = `/${shopname}/app/token`

  console.log(`[ShopRenter] Requesting token for ${shopname} using client_credentials`)

  const requestBody = JSON.stringify({
    grant_type: 'client_credentials',
    client_id: clientId,
    client_secret: clientSecret
  })

  const requestHeaders: Record<string, string> = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Content-Length': requestBody.length.toString()
  }

  try {
    // Use HTTP/1.0 request (required by ShopRenter API)
    const response = await makeHttp10Request(hostname, path, 'POST', requestHeaders, requestBody)

    if (response.status !== 200) {
      console.error('[ShopRenter] Token request error:', response.body)
      throw new Error(`Failed to get access token: ${response.status} ${response.body}`)
    }

    const data = JSON.parse(response.body)
    console.log(`[ShopRenter] Access token obtained for ${shopname}`)
    return data
  } catch (error) {
    console.error('[ShopRenter] Token request error:', error)
    throw new Error('Failed to get access token via client_credentials')
  }
}

// Refresh access token
async function refreshAccessToken(shopname: string, refreshToken: string, clientId: string, clientSecret: string) {
  if (!clientId || !clientSecret) {
    throw new Error('ShopRenter client credentials not provided')
  }

  // Use the same endpoint as token request: oauth.app.shoprenter.net
  const hostname = 'oauth.app.shoprenter.net'
  const path = `/${shopname}/app/token`

  const requestBody = JSON.stringify({
    grant_type: 'refresh_token',
    client_id: clientId,
    client_secret: clientSecret,
    refresh_token: refreshToken
  })

  const requestHeaders: Record<string, string> = {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Content-Length': requestBody.length.toString()
  }

  try {
    // Use HTTP/1.0 request (required by ShopRenter API)
    const response = await makeHttp10Request(hostname, path, 'POST', requestHeaders, requestBody)

    if (response.status !== 200) {
      console.error('[ShopRenter] Token refresh error:', response.body)
      throw new Error(`Failed to refresh token: ${response.status} ${response.body}`)
    }

    const data = JSON.parse(response.body)
    console.log(`[ShopRenter] Token refreshed for ${shopname}`)
    return data
  } catch (error) {
    console.error('[ShopRenter] Token refresh error:', error)
    throw error
  }
}

// Make authenticated API request to ShopRenter
export async function shopRenterApiRequest(
  storeId: string,
  endpoint: string,
  method: string = 'GET',
  body?: any
): Promise<any> {
  const supabaseUrl = Deno.env.get('SUPABASE_URL')!
  const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  const supabase = createClient(supabaseUrl, supabaseServiceKey)

  // Get store info
  const { data: store, error: storeError } = await supabase
    .from('stores')
    .select('store_name, store_url')
    .eq('id', storeId)
    .eq('platform_name', 'shoprenter')
    .single()

  if (storeError || !store) {
    throw new Error('ShopRenter store not found')
  }

  // Get valid access token
  const accessToken = await getValidAccessToken(storeId)

  // Build API URL components
  const hostname = `${store.store_name}.api2.myshoprenter.hu`
  const path = `/api${endpoint}`

  // Prepare headers
  const requestHeaders: Record<string, string> = {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }

  // Prepare body if needed
  let requestBody: string | undefined = undefined
  if (body && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
    requestBody = JSON.stringify(body)
    requestHeaders['Content-Length'] = requestBody.length.toString()
  }

  try {
    // Use HTTP/1.0 request (required by ShopRenter API)
    const response = await makeHttp10Request(hostname, path, method, requestHeaders, requestBody)

    if (response.status !== 200 && response.status !== 201) {
      console.error(`[ShopRenter] API error (${response.status}):`, response.body)

      // Handle 401 - token might be invalid
      if (response.status === 401) {
        throw new Error('Unauthorized - token may be invalid')
      }

      throw new Error(`API request failed with status ${response.status}`)
    }

    const data = JSON.parse(response.body)

    // Debug logging for order queries
    if (endpoint.includes('/orderExtend')) {
      console.log('[ShopRenter] Order API response:', {
        endpoint: path,
        dataType: Array.isArray(data) ? 'array' : typeof data,
        itemCount: Array.isArray(data) ? data.length :
                  (data.items ? data.items.length :
                  (data.data ? data.data.length :
                  (data.orders ? data.orders.length : 'unknown'))),
        responseKeys: Object.keys(data)
      })
    }

    return data
  } catch (error) {
    console.error('[ShopRenter] API request error:', error)
    throw error
  }
}

// Fetch products from ShopRenter
export async function fetchProducts(storeId: string, page: number = 0, limit: number = 25): Promise<any> {
  return shopRenterApiRequest(
    storeId,
    `/productExtend?page=${page}&limit=${limit}&full=1`,
    'GET'
  )
}

// Fetch a single category by ID from ShopRenter
export async function fetchCategory(storeId: string, categoryId: string): Promise<any> {
  return shopRenterApiRequest(
    storeId,
    `/categoryExtend/${categoryId}?full=1`,
    'GET'
  )
}

export interface ShopRenterOrderFilters {
  status?: string;           // Filter by order status
  email?: string;            // Filter by customer email (correct parameter name)
  createdAtMin?: string;     // ISO 8601 datetime - orders created after this date
  createdAtMax?: string;     // ISO 8601 datetime - orders created before this date
  updatedAtMin?: string;     // ISO 8601 datetime - orders updated after this date
  updatedAtMax?: string;     // ISO 8601 datetime - orders updated before this date
  innerId?: string;          // Filter by customer-visible order number (innerId)
}

export interface ShopRenterCustomerFilters {
  email?: string;    // Filter by customer email
  search?: string;   // Search by name or email
}

// Fetch customers from ShopRenter
export async function fetchCustomers(
  storeId: string,
  page: number = 0,
  limit: number = 25,
  filters?: ShopRenterCustomerFilters
): Promise<any> {
  let endpoint = `/customerExtend?page=${page}&limit=${limit}&full=1`

  if (filters) {
    if (filters.email) {
      endpoint += `&email=${encodeURIComponent(filters.email)}`
    }
    if (filters.search) {
      endpoint += `&search=${encodeURIComponent(filters.search)}`
    }
  }

  return shopRenterApiRequest(storeId, endpoint, 'GET')
}

// Fetch orders from ShopRenter
export async function fetchOrders(
  storeId: string,
  page: number = 0,
  limit: number = 25,
  filters?: ShopRenterOrderFilters
): Promise<any> {
  let endpoint = `/orderExtend?page=${page}&limit=${limit}&full=1`

  if (filters) {
    if (filters.status) {
      endpoint += `&status=${filters.status}`
    }
    if (filters.email) {
      endpoint += `&email=${encodeURIComponent(filters.email)}`
    }
    if (filters.createdAtMin) {
      endpoint += `&createdAtMin=${encodeURIComponent(filters.createdAtMin)}`
    }
    if (filters.createdAtMax) {
      endpoint += `&createdAtMax=${encodeURIComponent(filters.createdAtMax)}`
    }
    if (filters.updatedAtMin) {
      endpoint += `&updatedAtMin=${encodeURIComponent(filters.updatedAtMin)}`
    }
    if (filters.updatedAtMax) {
      endpoint += `&updatedAtMax=${encodeURIComponent(filters.updatedAtMax)}`
    }
    if (filters.innerId) {
      endpoint += `&innerId=${encodeURIComponent(filters.innerId)}`
    }
  }

  return shopRenterApiRequest(storeId, endpoint, 'GET')
}

// Fetch a single order by ID
export async function fetchOrder(storeId: string, orderId: string): Promise<ShopRenterOrder> {
  const response = await shopRenterApiRequest(storeId, `/orderExtend/${orderId}?full=1`, 'GET')
  return response
}

// Register webhook
export async function registerWebhook(storeId: string, event: string, callbackUrl: string): Promise<any> {
  return shopRenterApiRequest(
    storeId,
    '/webhooks',
    'POST',
    {
      event,
      address: callbackUrl,
      active: true
    }
  )
}

// List webhooks
export async function listWebhooks(storeId: string): Promise<any> {
  return shopRenterApiRequest(
    storeId,
    '/webhooks',
    'GET'
  )
}

// Delete webhook
export async function deleteWebhook(storeId: string, webhookId: string): Promise<any> {
  return shopRenterApiRequest(
    storeId,
    `/webhooks/${webhookId}`,
    'DELETE'
  )
}
