import { Next } from 'koa'
import { parse } from 'querystring'
import { Context } from '../../Router'
import { FAILED_CODE } from '../../utils'
import { isFormType, isJsonType } from './type'

function isHttp(event: UniCloudEvent, ctx: UniCloudContext) {
  const env = (ctx as any).env
  if (env && env.MP_SOURCE === 'http') {
    return true
  }
  return !!(event.httpMethod && event.headers)
}

type Data = { [key: string]: string }

const isObject = (field: unknown): field is Data => typeof field !== 'string'

const CONTENT_TYPE = 'content-type'
const JSON_TYPE = 'application/json'

function initContextType(headers: Data) {
  const key = Object.keys(headers).find(
    (key) => key.toLowerCase() === CONTENT_TYPE
  )
  if (key) {
    headers[CONTENT_TYPE] = headers[key].toLowerCase()
    if (key !== CONTENT_TYPE) {
      delete headers[key]
    }
  } else {
    headers[CONTENT_TYPE] = JSON_TYPE
  }
}

function extend(ctx: Context, isHttpRequest: boolean) {
  if (isHttpRequest) {
    const { headers, httpMethod, body, queryStringParameters } = ctx.event
    initContextType(headers)
    ctx.query = queryStringParameters
    if (httpMethod === 'GET') {
      ctx.data = ctx.query
    } else {
      ctx.data = Object.create(null)
      if (body) {
        const contentType = headers[CONTENT_TYPE]
        if (isJsonType(contentType)) {
          try {
            ctx.data = JSON.parse(body)
          } catch (e) {}
        } else if (isFormType(contentType)) {
          ctx.data = parse(body)
        }
      }
    }
  }
  ctx.set = function set(field: Data | string, val?: string | string[]) {
    if (arguments.length === 2) {
      if (Array.isArray(val)) {
        val = val.map((v) => (typeof v === 'string' ? v : String(v)))
      } else if (typeof val !== 'string') {
        val = String(val)
      }
      ctx.headers[field as string] = val as string
    } else if (isObject(field)) {
      for (const key in field) {
        ctx.set(key, field[key])
      }
    }
  }
}

export function parseAction(event: UniCloudEvent) {
  if (!event.action && event.path) {
    event.action = event.path.substr(1)
  }
  let action = String(event.action || '')
  if (action.startsWith('/')) {
    event.action = action = action.substr(1)
  }
  return action
}

export async function http(ctx: Context, next: Next) {
  const isHttpRequest = isHttp(ctx.event, ctx.context)
  extend(ctx, isHttpRequest)
  if (!isHttpRequest) {
    await next()
  } else {
    const contentTypeHeader = { [CONTENT_TYPE]: JSON_TYPE }
    try {
      await next()
    } catch (e) {
      const ret = {
        code: e.code || FAILED_CODE,
        message: e.message,
      }
      if (ctx.config.debug === true) {
        ;(ret as any).stack = e.stack || ''
      }
      return (ctx.body = {
        mpserverlessComposedResponse: true, // aliyun
        statusCode: 400,
        headers: contentTypeHeader,
        body: JSON.stringify(ret),
      })
    }
    const contextType = ctx.headers[CONTENT_TYPE] || JSON_TYPE
    ctx.body = {
      mpserverlessComposedResponse: true, // aliyun
      isBase64Encoded: !!ctx.isBase64Encoded,
      statusCode: ctx.status,
      headers: Object.assign(ctx.headers, { [CONTENT_TYPE]: contextType }),
      body: contextType === JSON_TYPE ? JSON.stringify(ctx.body) : ctx.body,
    }
  }
}