package com.pomelotea.embylisenceserver

import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.error.ErrorAttributes
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.http.HttpStatus
import org.springframework.validation.BindException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.*
import org.springframework.web.client.HttpServerErrorException
import org.springframework.web.servlet.NoHandlerFoundException
import org.springframework.web.servlet.view.RedirectView
import java.net.InetAddress
import java.net.UnknownHostException
import java.text.MessageFormat
import javax.servlet.ServletOutputStream
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse


@SpringBootApplication
class EmbyLisenceServerApplication

fun main(args: Array<String>) {
    runApplication<EmbyLisenceServerApplication>(*args)
}

@RequestMapping("/admin/service/registration")
@RestController
@CrossOrigin
class LisenceController {

    @RequestMapping("validateDevice", "validateDevice.php")
    fun validateDevice(): Device {
        return Device()
    }

    @RequestMapping("getStatus", "getStatus.php")
    fun getStatus(): Status {
        return Status()
    }

    @RequestMapping("validate", "validate.php")
    fun validate(): Validate {
        return Validate()
    }
}

data class Device(
    val cacheExpirationDays: Int = 365,
    val message: String = "Device Valid",
    val resultCode: String = "GOOD"
)

class Empty

data class Status(
    val deviceStatus: String = "0",
    val planType: String = "Lifetime",
    val subscriptions: Empty = Empty()
)

data class Validate(
    val featId: String = "MBSupporter",
    val registered: Boolean = true,
    val expDate: String = "2030-01-01",
    val key: Int = 114514
)


/**
 *
 * @version 0.0.1
 * @author jimlee
 * date 2022-07-18 10:51
 * 全局异常处理器
 **/
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
@RequestMapping("/error")
class ExtendExceptionHandler(errorAttributes: ErrorAttributes) : AbstractErrorController(errorAttributes) {

    /**
     * 参数断言异常
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ExceptionHandler(IllegalArgumentException::class)
    @Throws(Exception::class)
    fun illegalArgumentException(request: HttpServletRequest, response: HttpServletResponse, ex: Exception) {
        log.error(
            MessageFormat.format(
                "!!! request uri:{0} from {1} assert exception:{2}",
                request.requestURI,
                getIpAddr(request),
                ex.message
            ), ex
        )
        out(
            response,
            "${if (ex.message != null && ex.message!!.length > 40) ex.message!!.substring(0, 40) else ex.message}",
            false,
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            ex.javaClass.name
        )
    }

    /**
     * 空指针异常
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ExceptionHandler(NullPointerException::class)
    @Throws(Exception::class)
    fun nullPointerException(request: HttpServletRequest, response: HttpServletResponse, ex: Exception) {
        log.error(
            MessageFormat.format(
                "!!! request uri:{0} from {1} null pointor exception:{2}",
                request.requestURI,
                getIpAddr(request),
                ex.message
            ), ex
        )
        out(
            response,
            "服务器开小差了:${if (ex.message != null && ex.message!!.length > 40) ex.message!!.substring(0, 40) else ex.message}",
            false,
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            ex.javaClass.name
        )
    }

    /**
     * 其他异常
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ExceptionHandler(Exception::class)
    @Throws(Exception::class)
    fun unknownException(request: HttpServletRequest, response: HttpServletResponse, ex: Exception) {
        log.error(
            MessageFormat.format(
                "!!! request uri:{0} from {1} unknown exception:{2}",
                request.requestURI,
                getIpAddr(request),
                ex.message
            ), ex
        )
        out(
            response,
            "服务器错误:${if (ex.message != null && ex.message!!.length > 40) ex.message!!.substring(0, 40) else ex.message}",
            false,
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            ex.javaClass.name
        )
    }

    /**
     * 500状态异常
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(HttpServerErrorException.InternalServerError::class)
    @Throws(Exception::class)
    fun serverError(request: HttpServletRequest, response: HttpServletResponse, ex: Exception) {
        log.error(
            MessageFormat.format(
                "!!! request uri:{0} from {1} server error[500] exception:{2}",
                request.requestURI,
                getIpAddr(request),
                ex.message
            ), ex
        )
        out(
            response,
            "服务器内部错误:${if (ex.message != null && ex.message!!.length > 40) ex.message!!.substring(0, 40) else ex.message}",
            false,
            HttpStatus.INTERNAL_SERVER_ERROR.value(),
            ex.javaClass.name
        )
    }

    /**
     * 404状态异常
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException::class)
    @Throws(Exception::class)
    fun notFound(request: HttpServletRequest, response: HttpServletResponse, ex: Exception) {
        log.error(
            MessageFormat.format(
                "!!! request uri:{0} from {1} not found mapping url exception:{2}",
                request.requestURI,
                getIpAddr(request),
                ex.message
            ), ex
        )
        out(
            response,
            "404 Not Fount:${if (ex.message != null && ex.message!!.length > 40) ex.message!!.substring(0, 40) else ex.message}",
            false,
            HttpStatus.NOT_FOUND.value(),
            ex.javaClass.name
        )
    }

    /**
     * 请求校验缺少必要参数
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException::class)
    @Throws(Exception::class)
    fun methodArgumentNotValidException(request: HttpServletRequest, response: HttpServletResponse, ex: MethodArgumentNotValidException) {
        val msg = StringBuilder()
        ex.bindingResult.fieldErrors.forEach {
            msg.append(
                "[${it.field}]:${it.defaultMessage}"
            )
        }
        out(
            response,
            msg.toString(),
            false,
            HttpStatus.NOT_FOUND.value(),
            ex.javaClass.name
        )
    }

    /**
     * 参数绑定请求实体错误
     * @param request HttpServletRequest        请求
     * @param response HttpServletResponse      响应
     * @param ex Exception                      服务异常
     * @throws Exception                        方法抛出异常
     */
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException::class)
    @Throws(Exception::class)
    fun bindException(request: HttpServletRequest, response: HttpServletResponse, ex: BindException) {
        val msg = StringBuilder()
        ex.bindingResult.fieldErrors.forEach {
            msg.append(
                "[${it.field}]:${it.defaultMessage}"
            )
        }
        out(
            response,
            msg.toString(),
            false,
            HttpStatus.NOT_FOUND.value(),
            ex.javaClass.name
        )
    }

    @RequestMapping
    @ResponseBody
    @Throws(Exception::class)
    fun handleErrors(request: HttpServletRequest, response: HttpServletResponse, e: Exception) {
        val status = getStatus(request)
        if (status === HttpStatus.NOT_FOUND) {
            notFound(request, response, e)
        } else if (status === HttpStatus.INTERNAL_SERVER_ERROR) {
            serverError(request, response, e)
        } else unknownException(request, response, e)
    }

    @GetMapping
    @ResponseBody
    @Throws(Exception::class)
    fun defaultGetError(request: HttpServletRequest, response: HttpServletResponse, e: Exception) {
        handleErrors(request, response, e)
    }

    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    @RequestMapping(produces = ["text/html"])
    @Throws(Exception::class)
    fun handleHtml(request: HttpServletRequest, response: HttpServletResponse): RedirectView {
        return RedirectView("/404")
    }




    companion object {
        private val log: Logger = LoggerFactory.getLogger(ExtendExceptionHandler::class.java)

        /**
         * 全局异常错误输出
         * @param response  响应对象
         * @param msg       消息
         * @param success   状态
         * @param code      响应码
         * @param data      响应实体
         */
        @JvmStatic
        fun out(response: HttpServletResponse, msg: String?, success: Boolean, code: Int?, data: Any? = null) {
            val out: ServletOutputStream
            try {
                out = response.outputStream
                response.reset()
                response.characterEncoding = "UTF-8"
                response.contentType = "application/json;charset=utf-8"
                response.status = HttpStatus.OK.value()
                out.write(ObjectMapper().writeValueAsBytes(WebResult.getWebResult(success, data, msg, code)))
            } catch (e: Exception) {
                log.error(e.toString() + "响应错误")
            }
        }

        /**
         * 获取请求IP地址
         * @param request 请求
         * @return String?  ip地址
         */
        fun getIpAddr(request: HttpServletRequest): String? {
            var ipAddress: String?
            return try {
                ipAddress = request.getHeader("x-forwarded-for")
                if (ipAddress.isNullOrEmpty() || "unknown".equals(ipAddress, ignoreCase = true)) {
                    ipAddress = request.getHeader("Proxy-Client-IP")
                }
                if (ipAddress.isNullOrEmpty() || "unknown".equals(ipAddress, ignoreCase = true)) {
                    ipAddress = request.getHeader("WL-Proxy-Client-IP")
                }
                if (ipAddress.isNullOrEmpty() || "unknown".equals(ipAddress, ignoreCase = true)) {
                    ipAddress = request.remoteAddr
                    // 本机访问, 获取网卡地址
                    if (ipAddress == "127.0.0.1" || ipAddress == "0:0:0:0:0:0:0:1") {
                        try {
                            ipAddress = InetAddress.getLocalHost().hostAddress
                            if (ipAddress != null) {
                                ipAddress += "(local)"
                            }
                        } catch (e: UnknownHostException) {
                            log.error("获取本机网卡IP失败", e)
                        }
                    }
                }
                if (ipAddress != null) {
                    if (ipAddress.contains(",")) {
                        ipAddress.split(",".toRegex()).toTypedArray()[0]
                    } else {
                        ipAddress
                    }
                } else {
                    log.error("获取请求IP为空")
                    null
                }
            } catch (e: Exception) {
                log.error("获取用户IP失败", e)
                null
            }
        }
    }
}


class WebResult<T> : java.io.Serializable {
    var data: T? = null
    var code: Int? = null
    var success = false
    var message: String? = null

    @Suppress("UNUSED")
    var timestamp = System.currentTimeMillis()

    companion object {

        fun <T> getWebResult(success: Boolean, data: T, message: String?, code: Int?): WebResult<T> {
            val webResult = WebResult<T>()
            webResult.success = success
            webResult.data = data
            webResult.code = code
            webResult.message = message
            return webResult
        }
    }
}
