use cfg_if::cfg_if;
use leptos::*;
use validator::Validate;
use crate::backend::data::{ApiResponse, Appearance};
use crate::components::data_form::ForValidation;

cfg_if! { if #[cfg(feature = "ssr")] {

    use actix_web::{post, Responder};
    use actix_multipart::Multipart;
    use actix_session::Session;
    use actix_web::web::Redirect;
    use std::fs::File;
    use std::io::{Write, Read};
    use futures_util::{StreamExt, TryStreamExt};
    use crate::backend::data::User;
    use crate::error::AppError;
    use sqlx::{query, query_as, PgPool};
    use crate::backend::get_pool;
    use actix_web::web::Data;
    use crate::backend::AppData;
    use regex::Regex;

    pub async fn check_appearance(pool: &PgPool) -> Result<(), AppError> {
        let count: (i64,) = query_as("SELECT COUNT(id) FROM appearance")
            .fetch_one(pool)
            .await?;

        if count.0 == 0 {
            query("INSERT INTO appearance(title) VALUES('Rezervovator')")
                .execute(pool)
                .await?;
        }

        Ok(())
    }

    async fn set_banner_name(file_name: &str, pool: &PgPool) -> Result<(), AppError> {
        query("UPDATE appearance SET banner = $1")
            .bind(file_name)
            .execute(pool)
            .await?;

        Ok(())
    }

    async fn modify_style(file_name: &str) -> Result<(), AppError> {
        let mut css_file = File::open("target/site/banner.css")?;
        let mut css_str= String::new();
        css_file.read_to_string(&mut css_str)?;

        if css_str.contains("/*bg-img*/") {
            css_str = css_str.replace("/*bg-img*/", &format!("background-image: url('{}');", file_name));
        } else {
            let re = Regex::new(r#"background-image: url\('[aA-zZ._0-9\-]+'\)"#).unwrap();
            css_str = re.replace(&css_str, &format!("background-image: url('{}')", file_name)).to_string();
        }

        let mut css_file = File::create("target/site/banner.css")?;
        css_file.write_all(css_str.as_bytes())?;

        Ok(())
    }

    #[post("/upload")]
    pub async fn upload(mut data: Multipart, session: Session, app_data: Data<AppData>) -> impl Responder {
        let user = session.get::<User>("user").unwrap_or(None);

        if user.is_none() {
            return Redirect::to("/login").see_other();
        }

        if let Some(u) = user {
            if !u.admin {
                return Redirect::to("/admin/appearance").see_other();
            }
        }

        if let Ok(Some(mut field)) = data.try_next().await {
            let content_disp = field.content_disposition().clone();
            let file_name = content_disp.get_filename().unwrap();

            if file_name.is_empty() {
                return Redirect::to("/admin/appearance").see_other();
            }

            let mut file = File::create(format!("target/site/{}", file_name)).unwrap();
            let _name = field.name();
            while let Some(chunk) = field.next().await {
                let c = chunk.unwrap();
                let _ = file.write_all(&c);
            }
            let _ = set_banner_name(file_name, &app_data.db_pool).await;
            let _ = modify_style(file_name).await;
        }

        Redirect::to("/admin/appearance").see_other()
    }

}}

#[server]
pub async fn get_appearance() -> Result<Appearance, ServerFnError> {
    let pool = get_pool().await?;
    let appearance = query_as::<_, Appearance>("SELECT * FROM appearance")
        .fetch_one(&pool)
        .await?;

    Ok(appearance)
}

#[server]
pub async fn update_appearance(appearance: Appearance) -> Result<ApiResponse<()>, ServerFnError> {
    use crate::perm_check;

    perm_check!(is_admin);

    let pool = get_pool().await?;
    let id = appearance.id();
    query("UPDATE appearance SET title = $1, text = $2 WHERE id = $3")
        .bind(appearance.title)
        .bind(appearance.text)
        .bind(id)
        .execute(&pool)
        .await?;

    Ok(ApiResponse::Data(()))
}

impl ForValidation for UpdateAppearance {
    fn entity(&self) -> &dyn Validate {
        &self.appearance
    }
}

#[server]
pub async fn delete_banner() -> Result<ApiResponse<()>, ServerFnError> {
    use std::fs;
    use crate::perm_check;

    perm_check!(is_admin);

    let appearance = get_appearance().await?;
    let pool = get_pool().await?;
    query("UPDATE appearance SET banner = $1 WHERE id = $2")
        .bind(None::<String>)
        .bind(appearance.id())
        .execute(&pool)
        .await?;

    if let Some(f) = appearance.banner {
        fs::remove_file(format!("target/site/{}", f))?;
    }

    Ok(ApiResponse::Data(()))
}