import type { Buffer } from 'buffer'
import { AwsClient } from 'aws4fetch'
import { claimUploadUrlStatus, createUploadUrlStatus } from '@/src/dbService'
import {
  getS3AccessKeyId,
  getS3Region,
  getS3Url,
  getS3SecretAccessKey,
  getS3BucketName,
  getAssetsDomain,
} from '@/src/lib/env-exporter'

let awsClient: AwsClient | null = null
let awsClientKey = ''

const getAwsClient = () => {
  const region = getS3Region()
  const endpoint = getS3Url()
  const accessKeyId = getS3AccessKeyId()
  const secretAccessKey = getS3SecretAccessKey()
  const nextKey = `${region}|${endpoint}|${accessKeyId}|${secretAccessKey}`

  if (!awsClient || nextKey !== awsClientKey) {
    awsClientKey = nextKey
    awsClient = new AwsClient({
      accessKeyId,
      secretAccessKey,
      region,
      service: 's3',
    })
  }

  return awsClient
}

const buildObjectUrl = (bucket: string, key: string) => {
  const endpoint = getS3Url()
  const normalizedEndpoint = endpoint.endsWith('/')
    ? endpoint.slice(0, -1)
    : endpoint
  const normalizedKey = key.replace(/^\/+/, '')
  return `${normalizedEndpoint}/${bucket}/${normalizedKey}`
}

export const imageUploadS3 = async(body: Buffer, type: string, key: string) => {
  const client = getAwsClient()
  const url = buildObjectUrl(getS3BucketName(), key)
  const resp = await client.fetch(url, {
    method: 'PUT',
    headers: {
      'Content-Type': type,
    },
    body,
  })
  if (!resp.ok) {
    const responseBody = await resp.text().catch(() => '')
    throw new Error(`Failed to upload image: ${resp.status} ${responseBody}`)
  }
  const imageUrl = `${key}`
  return imageUrl
}

// export async function deleteImageUtil(...keys:string[]):Promise<boolean>;

export async function deleteImageUtil({bucket = getS3BucketName(), keys}:{bucket?: string, keys: string[]}) {
  if (keys.length === 0) {
    return true
  }
  try {
    const client = getAwsClient()
    await Promise.all(
      keys.map(async (key) => {
        const url = buildObjectUrl(bucket, key)
        const resp = await client.fetch(url, { method: 'DELETE' })
        if (!resp.ok && resp.status !== 404) {
          const body = await resp.text().catch(() => '')
          throw new Error(`Failed to delete image: ${resp.status} ${body}`)
        }
      })
    )
    return true
  }
  catch (error) {
    console.error('Error deleting image:', error)
    throw new Error('Failed to delete image')
  }
}

export function scaffoldAssetUrl(input: string | null): string
export function scaffoldAssetUrl(input: (string | null)[]): string[]
export function scaffoldAssetUrl(input: string | null | (string | null)[]): string | string[] {
  const assetsDomain = getAssetsDomain()
  if (Array.isArray(input)) {
    return input.map(key => scaffoldAssetUrl(key) as string)
  }
  if (!input) {
    return ''
  }
  const normalizedKey = input.replace(/^\/+/, '')
  const domain = assetsDomain.endsWith('/')
    ? assetsDomain.slice(0, -1)
    : assetsDomain
  return `${domain}/${normalizedKey}`
}

/**
 * Generate a signed URL from an S3 URL
 * @param s3Url The full S3 URL (e.g., https://bucket-name.s3.region.amazonaws.com/path/to/object)
 * @param expiresIn Expiration time in seconds (default: 259200 seconds = 3 days)
 * @returns A pre-signed URL that provides temporary access to the object
 */
export async function generateSignedUrlFromS3Url(s3UrlRaw: string|null, expiresIn: number = 259200): Promise<string> {
  if (!s3UrlRaw) {
    return ''
  }

  const s3Url = s3UrlRaw
  
  try {
    const client = getAwsClient()
    const url = buildObjectUrl(getS3BucketName(), s3Url)
    const signedRequest = await client.sign(url, {
      method: 'GET',
      signQuery: true,
      expires: expiresIn,
    })
    return signedRequest.url
  } catch (error) {
    console.error('Error generating signed URL:', error)
    throw new Error('Failed to generate signed URL')
  }
}

/**
 * Get the original S3 URL from a signed URL
 * @param signedUrl The signed URL 
 * @returns The original S3 URL if found in cache, otherwise null
 */
export function getOriginalUrlFromSignedUrl(signedUrl: string|null): string|null {
  // Cache disabled for Workers compatibility - cannot retrieve original URL without cache
  // To re-enable, migrate signed-url-cache to object storage (R2/S3)
  return null
}

/**
 * Generate signed URLs for multiple S3 URLs
 * @param s3Urls Array of S3 URLs or null values
 * @param expiresIn Expiration time in seconds (default: 259200 seconds = 3 days)
 * @returns Array of signed URLs (empty strings for null/invalid inputs)
 */
export async function generateSignedUrlsFromS3Urls(s3Urls: (string|null)[], expiresIn: number = 259200): Promise<string[]> {
  if (!s3Urls || !s3Urls.length) {
    return []
  }

  try {
    const signedUrls = await Promise.all(
      s3Urls.map(url => generateSignedUrlFromS3Url(url, expiresIn).catch(() => ''))
    )
    
    return signedUrls
  } catch (error) {
    console.error('Error generating multiple signed URLs:', error)
    return s3Urls.map(() => '')
  }
}

export async function generateUploadUrl(key: string, mimeType: string, expiresIn: number = 180): Promise<string> {
  try {
    await createUploadUrlStatus(key)

    const client = getAwsClient()
    const url = buildObjectUrl(getS3BucketName(), key)
    const signedRequest = await client.sign(url, {
      method: 'PUT',
      signQuery: true,
      expires: expiresIn,
      headers: {
        'Content-Type': mimeType,
      },
    })

    return signedRequest.url
  } catch (error) {
    console.error('Error generating upload URL:', error)
    throw new Error('Failed to generate upload URL')
  }
}

// export function extractKeyFromPresignedUrl(url:string) {
//   const u = new URL(url);
//   const rawKey = u.pathname.replace(/^\/+/, ""); // remove leading slash
//   return decodeURIComponent(rawKey);
// }

// New function (excludes bucket name)
export function extractKeyFromPresignedUrl(url: string): string {
  const u = new URL(url)
  const rawKey = u.pathname.replace(/^\/+/, '') // remove leading slash
  const decodedKey = decodeURIComponent(rawKey)
  // Remove bucket prefix
  const parts = decodedKey.split('/')
  parts.shift() // Remove bucket name
  return parts.join('/')
}

export async function claimUploadUrl(url: string): Promise<void> {
  try {
    const semiKey = extractKeyFromPresignedUrl(url)

    // Update status to 'claimed' if currently 'pending'
    const updated = await claimUploadUrlStatus(semiKey)
    
    if (!updated) {
      throw new Error('Upload URL not found or already claimed')
    }
  } catch (error) {
    console.error('Error claiming upload URL:', error)
    throw new Error('Failed to claim upload URL')
  }
}
