use crate::base64::{encode_file, sha_1};
use crate::stream::{Protocol, Scheme};
use crate::config::{Config};
use crate::encoding::{compress_to_gzip, Encoding};
use crate::request::{Method, Request};
use chrono::{DateTime, Duration, Utc};
use json::{object, JsonValue};
use log::debug;
use std::path::{Path};
use std::{fs, io};
use std::sync::{Arc, Mutex};
use urlencoding::encode;
use crate::Handler;

/// 响应
#[derive(Clone, Debug)]
pub struct Response {
    pub config: Config,
    pub status: Status,
    pub headers: JsonValue,
    pub cookies: JsonValue,
    /// 消息体
    pub body: Vec<u8>,
    pub body_text: JsonValue,
    /// HTTP2 使用
    pub stream_id: u32,
    /// ws 通信密钥
    pub key: String,
    /// ws 版本
    pub version: String,
    method: Method,
    pub scheme: Arc<Mutex<Scheme>>,
    accept_encoding: Encoding,
}
impl Response {
    pub fn default(config: Config, scheme: Arc<Mutex<Scheme>>) -> Self {
        Self {
            config,
            status: Status::default(),
            headers: object! {},
            cookies: object! {},
            body: vec![],
            body_text: JsonValue::Null,
            stream_id: 0,
            key: String::new(),
            version: String::new(),
            method: Method::None,
            scheme,
            accept_encoding: Encoding::None,
        }
    }
    pub fn status(&mut self, code: i32) -> &mut Self {
        self.status.set_code(code);
        self
    }
    /// 跳转
    pub fn location(&mut self, uri: &str) -> &mut Self {
        self.header("Location", uri);
        self
    }
    /// 设置 HOST
    pub fn set_host(&mut self, host: &str) -> &mut Self {
        self.header("Host", host);
        self
    }
    /// 设置 请求内容
    pub fn set_request(&mut self, request: &mut Request) {
        self.config = request.config.clone();
        self.status.protocol = request.protocol.clone();
        self.method = request.method.clone();
        self.accept_encoding = request.accept_encoding.clone();
    }
    pub fn header(&mut self, key: &str, value: &str) -> &mut Self {
        self.headers[key] = value.into();
        self
    }
    pub fn cookie(&mut self, key: &str, value: &str) -> &mut Self {
        self.cookies[key] = value.into();
        self
    }
    pub fn update_body(&mut self) {
        self.body = self.body_text.to_string().into_bytes();
    }
    pub fn get_date(&mut self, s: i64) -> String {
        let utc: DateTime<Utc> = Utc::now();
        let future = utc + Duration::seconds(s); // 加 3600 秒 = 1 小时
        future.format("%a, %d %b %Y %H:%M:%S GMT").to_string()
    }
    /// HTML 返回
    pub fn html(&mut self, value: &str) -> &mut Self {
        if self.config.charset.is_empty() {
            self.header("Content-Type", format!("{};", Extension::form("html").as_str()).as_str());
        } else {
            self.header("Content-Type", format!("{}; charset={}", Extension::form("html").as_str(), self.config.charset).as_str());
        }
        self.body = value.to_string().into_bytes();
        self.body_text = value.into();
        self
    }
    /// TXT 返回
    pub fn txt(&mut self, value: &str) -> &mut Self {
        if self.config.charset.is_empty() {
            self.header("Content-Type", Extension::form("txt").as_str().to_string().as_str());
        } else {
            self.header("Content-Type", format!("{}; charset={}", Extension::form("txt").as_str(), self.config.charset).as_str());
        }
        self.body = value.to_string().into_bytes();
        self.body_text = value.into();
        self
    }
    /// JSON 返回
    pub fn json(&mut self, value: JsonValue) -> &mut Self {
        if self.config.charset.is_empty() {
            self.header("Content-Type", Extension::form("json").as_str().to_string().as_str());
        } else {
            self.header("Content-Type", format!("{}; charset={}", Extension::form("json").as_str(), self.config.charset).as_str());
        }
        self.body = value.to_string().into_bytes();
        self.body_text = value;
        self
    }
    /// 下载 返回
    ///
    /// # Panics
    ///
    /// This function will panic if:
    /// - `filename.extension()` returns `None`
    /// - The extension cannot be converted to a valid UTF-8 string
    /// - The filename cannot be converted to a valid UTF-8 string
    pub fn download(&mut self, filename: &Path) -> &mut Self {
        let Ok(file) = fs::read(filename) else {
            self.status(404);
            return self;
        };
        let extension = filename.extension().unwrap().to_str().unwrap().to_lowercase();

        if self.config.charset.is_empty() {
            self.header("Content-Type", Extension::form(extension.as_str()).as_str().to_string().as_str());
        } else {
            self.header("Content-Type", format!("{}; charset={}", Extension::form(extension.as_str()).as_str(), self.config.charset).as_str());
        }

        let encoded_file_name = encode(filename.file_name().unwrap().to_str().unwrap());
        self.header("Content-Disposition", format!(r"attachment; filename={encoded_file_name}").as_str());
        let digest = md5::compute(file.clone());
        self.header("Cache-Control", "no-cache");
        self.header("ETag", format!("{digest:x}").as_str());
        self.body = file;
        self
    }
    /// Serves a file for inline display in the browser.
    ///
    /// # Panics
    ///
    /// This function will panic if the file extension cannot be converted to a string,
    /// or if the filename cannot be converted to a valid UTF-8 string.
    pub fn file(&mut self, filename: &Path) -> &mut Self {
        let Ok(file) = fs::read(filename) else {
            self.status(404);
            return self;
        };

        let extension = match filename.extension() {
            None => String::new(),
            Some(e) => e.to_str().unwrap().to_lowercase(),
        };
        if self.config.charset.is_empty() {
            self.header("Content-Type", Extension::form(extension.as_str()).as_str().to_string().as_str());
        } else {
            self.header("Content-Type", format!("{}; charset={}", Extension::form(extension.as_str()).as_str(), self.config.charset).as_str());
        }
        self.header("Content-Disposition", format!(r#"inline; filename="{}""#, filename.file_name().unwrap().to_str().unwrap()).as_str());
        //let digest = md5::compute(file.clone());
        //self.header("Cache-Control", "no-cache");
        //self.header("ETag", format!("{digest:x}").as_str());
        if self.config.max_age > 0 {
            self.header("Cache-Control", format!("public, max-age={}", self.config.max_age).as_str());
            self.header("Expires", &self.clone().get_date(self.config.max_age));
        } else {
            self.header("Cache-Control", "no-cache");
        }

        self.body = file;
        self
    }
    pub fn websocket(&mut self, key: &str) -> &mut Self {
        self.header("Upgrade", "websocket");
        self.header("Connection", "Upgrade");
        let sha_code = sha_1(format!("{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11").as_str());
        let sec_websocket_accept = encode_file(sha_code);
        self.header("Sec-WebSocket-Accept", sec_websocket_accept.leak());
        self
    }
    /// Sends the response over the given scheme.
    ///
    /// # Errors
    ///
    /// Returns an error if writing the response over the scheme fails.
    pub fn send(&mut self) -> io::Result<()> {
        let mut header = vec![];
        header.push(self.status.text());

        header.push(format!("Date: {}", self.get_date(0)));

        // 服务器标识设置
        if !self.config.server.is_empty() {
            self.header("Server", self.config.clone().server.as_str());
        }


        for (key, value) in self.headers.entries() {
            header.push(format!("{key}: {value}"));
        }

        for (key, value) in self.cookies.entries() {
            header.push(format!("Set-Cookie: {key}={value}; Path=/; HttpOnly; SameSite=Lax"));
        }
        let body = if let Method::HEAD = self.method {
            String::new()
        } else {
            if !self.body.is_empty() {
                header.push(format!("Content-Length: {}", self.body.len()));
            }
            if self.config.gzip {
                match self.accept_encoding.clone() {
                    Encoding::None => {}
                    _ => {
                        self.header("Content-Encoding", self.accept_encoding.clone().str());
                    }
                }
                match self.accept_encoding {
                    Encoding::Deflate | Encoding::Gzip => {
                        match compress_to_gzip(&self.body.clone()) {
                            Ok(e) => unsafe { String::from_utf8_unchecked(e) },
                            Err(_) => unsafe { String::from_utf8_unchecked(self.body.clone()) },
                        }
                    }
                    _ => unsafe { String::from_utf8_unchecked(self.body.clone()) }, // Encoding::Br => {}
                }
            } else {
                unsafe { String::from_utf8_unchecked(self.body.clone()) }
            }
        };
        if self.config.debug {
            debug!("\r\n=================响应信息=================\r\n{}\r\n===========================================",header.join("\r\n"));
        }
        let text = format!("{}\r\n\r\n{}", header.join("\r\n"), body);

        self.scheme.lock().unwrap().write(text.as_bytes())?;
        Ok(())
    }
}
impl Handler for Response {
    fn on_options(&mut self, response: &mut Response) {
        if !response.headers.has_key("Access-Control-Allow-Origin") {
            response.header("Access-Control-Allow-Origin", "*");
        }
        // GET,POST,OPTIONS
        if !response.headers.has_key("Access-Control-Allow-Methods") {
            response.header("Access-Control-Allow-Methods", "*");
        }
        // Content-Type, Authorization, X-Real-IP,X-Forwarded-For
        if response.headers.has_key("Access-Control-Allow-Headers") {
            let headers = response.headers["Access-Control-Allow-Headers"].to_string();
            response.header("Access-Control-Allow-Headers", format!("Content-Type,Authorization,X-Real-IP,X-Forwarded-For,{headers}").as_str());
        } else {
            response.header("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Real-IP,X-Forwarded-For");
        }
        // 允许前端跨域请求时发送和接收 cookie / 凭据
        if !response.headers.has_key("Access-Control-Expose-Headers") {
            response.header("Access-Control-Expose-Headers", "true");
        }
        // 允许前端访问请求头
        if !response.headers.has_key("Access-Control-Expose-Headers") {
            response.header("Access-Control-Expose-Headers", "Content-Disposition");
        }
        if !response.headers.has_key("Access-Control-Max-Age") {
            response.header("Access-Control-Max-Age", "0");
        }
    }
}
#[derive(Clone, Debug)]
pub struct Status {
    pub code: i32,
    protocol: Protocol,
    reason: String,
}
impl Status {
    pub fn set_code(&mut self, code: i32) {
        self.code = code;
        self.reason = match code {
            100 => "Continue",
            101 => "Switching Protocols",
            102 => "Processing",
            200 => "OK",
            201 => "Created",
            204 => "No Content",
            301 => "Permanently Moved", // 重定向
            302 => "Move temporarily",
            403 => "Forbidden",
            404 => "Not Found", //服务器无法根据客户端的请求找到资源（网页）。通过此代码，网站设计人员可设置"您所请求的资源无法找到"的个性页面
            500 => "Internal Server Error", //服务器内部错误，无法完成请求
            501 => "Not Implemented", //服务器不支持请求的功能，无法完成请求
            502 => "Bad Gateway", //作为网关或者代理工作的服务器尝试执行请求时，从远程服务器接收到了一个无效的响应
            503 => "Service Unavailable", //由于超载或系统维护，服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
            504 => "Gateway Time-out",    //充当网关或代理的服务器，未及时从远端服务器获取请求
            505 => "HTTP Version Not Supported", //服务器不支持请求的HTTP协议的版本，无法完成处理
            _ => "",
        }.to_string();
    }
    pub fn text(&mut self) -> String {
        format!("{} {} {}", self.protocol.str(), self.code, self.reason)
    }
}
impl Default for Status {
    fn default() -> Self {
        Self {
            code: 200,
            protocol: Protocol::HTTP1_1,
            reason: "OK".to_string(),
        }
    }
}

/// 文件类型
enum Extension {}
impl Extension {
    pub fn form(extension: &str) -> String {
        match extension {
            "html" => "text/html",
            "css" => "text/css",
            "js" => "application/javascript",
            "json" => "application/json",
            "png" => "image/png",

            "jpg" | "jpeg" => "image/jpeg",
            "gif" => "image/gif",
            "txt" => "text/plain",
            "pdf" => "application/pdf",
            "svg" => "image/svg+xml",
            "woff" => "application/font-woff",
            "woff2" => "application/font-woff2",
            "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            _ => "application/octet-stream",
        }.to_string()
    }
}
