import crypto from 'node:crypto'
import fs from 'node:fs/promises'
import path from 'node:path'

import { Router } from 'express'

import { PROJECT_ROOT } from '../../config/runtime.js'
import {
  listAdminCloudtentaclesDeliveryRecords,
  listAdminCloudtentaclesRecordSources,
} from '../../services/admin/platform-config/cloudtentacles/index.js'
import { createJsonHandler, requireAdminRoles } from './session.js'
import { createHttpError, sendRouteError } from '../../utils/http.js'
import type { AdminCloudtentaclesDeliveryRecordQueryRouteBody } from '../../types/admin/route-inputs.js'

const router = Router()
const RECORD_IMAGE_CACHE_DIR = path.join(
  PROJECT_ROOT,
  'data',
  'cache',
  'cloudtentacles-record-images',
)

router.get(
  '/cloudtentacles-records/sources',
  requireAdminRoles(['admin', 'operator', 'support']),
  createJsonHandler(() => listAdminCloudtentaclesRecordSources(), {
    successMessage: 'ok',
    errorMessage: '读取 cloudtentacles 账号列表失败',
    scope: '[admin/cloudtentacles-records/sources]',
  }),
)

router.post(
  '/cloudtentacles-records',
  requireAdminRoles(['admin', 'operator', 'support']),
  createJsonHandler(
    (req) =>
      listAdminCloudtentaclesDeliveryRecords(
        req.body as AdminCloudtentaclesDeliveryRecordQueryRouteBody,
      ),
    {
      successMessage: 'cloudtentacles 发货记录查询成功',
      errorMessage: 'cloudtentacles 发货记录查询失败',
      scope: '[admin/cloudtentacles-records]',
    },
  ),
)

router.get(
  '/cloudtentacles-records/image',
  requireAdminRoles(['admin', 'operator', 'support']),
  async (req, res) => {
    try {
      const imageUrl = normalizeAllowedImageUrl(req.query.url)
      const cachedImage = await getCachedRecordImage(imageUrl)
      res.setHeader('Content-Type', cachedImage.contentType)
      res.setHeader('Cache-Control', 'public, max-age=86400')
      res.send(cachedImage.buffer)
    } catch (error) {
      sendRouteError(
        res,
        error,
        '读取 cloudtentacles 商品图片失败',
        '[admin/cloudtentacles-records/image]',
      )
    }
  },
)

export default router

function normalizeAllowedImageUrl(value: unknown) {
  const raw = String(value || '').trim()
  if (!raw) {
    throw createHttpError('缺少商品图片地址', {
      statusCode: 400,
      errorCode: 'cloudtentacles_record_image_url_required',
    })
  }

  const url = new URL(raw)
  if (!['https:', 'http:'].includes(url.protocol) || url.hostname !== 'gp.playinjoy.com') {
    throw createHttpError('商品图片地址不在允许范围内', {
      statusCode: 400,
      errorCode: 'cloudtentacles_record_image_url_not_allowed',
    })
  }

  return url.toString()
}

async function getCachedRecordImage(imageUrl: string) {
  const cacheMeta = buildRecordImageCacheMeta(imageUrl)
  const cached = await readCachedRecordImage(cacheMeta)
  if (cached) {
    return cached
  }

  const downloaded = await downloadRecordImage(imageUrl)
  const filePath = cacheMeta.filePathForContentType(downloaded.contentType)
  await fs.mkdir(RECORD_IMAGE_CACHE_DIR, { recursive: true })
  await fs.writeFile(filePath, downloaded.buffer)
  return downloaded
}

async function readCachedRecordImage(cacheMeta: ReturnType<typeof buildRecordImageCacheMeta>) {
  for (const candidate of cacheMeta.candidateFilePaths) {
    try {
      const buffer = await fs.readFile(candidate.filePath)
      return {
        buffer,
        contentType: candidate.contentType,
      }
    } catch (error) {
      const code = String((error as NodeJS.ErrnoException).code || '')
      if (code !== 'ENOENT') {
        throw error
      }
    }
  }

  return null
}

async function downloadRecordImage(imageUrl: string) {
  const response = await fetch(imageUrl, {
    headers: {
      accept: 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
      referer: 'https://gp.playinjoy.com/',
      'user-agent':
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0 Safari/537.36',
    },
  })

  if (!response.ok) {
    throw createHttpError(`商品图片读取失败，HTTP ${response.status}`, {
      statusCode: 502,
      errorCode: 'cloudtentacles_record_image_fetch_failed',
    })
  }

  const contentType = response.headers.get('content-type') || 'image/jpeg'
  if (!contentType.startsWith('image/')) {
    throw createHttpError('商品图片响应格式无效', {
      statusCode: 502,
      errorCode: 'cloudtentacles_record_image_invalid_content_type',
    })
  }

  return {
    buffer: Buffer.from(await response.arrayBuffer()),
    contentType: normalizeImageContentType(contentType),
  }
}

function buildRecordImageCacheMeta(imageUrl: string) {
  const digest = crypto.createHash('sha256').update(imageUrl).digest('hex')
  const candidateTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']
  return {
    candidateFilePaths: candidateTypes.map((contentType) => ({
      contentType,
      filePath: path.join(
        RECORD_IMAGE_CACHE_DIR,
        `${digest}.${extensionForContentType(contentType)}`,
      ),
    })),
    filePathForContentType(contentType: string) {
      return path.join(RECORD_IMAGE_CACHE_DIR, `${digest}.${extensionForContentType(contentType)}`)
    },
  }
}

function normalizeImageContentType(contentType: string) {
  return (
    String(contentType || 'image/jpeg')
      .split(';')[0]
      ?.trim()
      .toLowerCase() || 'image/jpeg'
  )
}

function extensionForContentType(contentType: string) {
  const normalized = normalizeImageContentType(contentType)
  if (normalized === 'image/png') return 'png'
  if (normalized === 'image/webp') return 'webp'
  if (normalized === 'image/gif') return 'gif'
  return 'jpg'
}
