use actix_http::StatusCode;
use actix_identity::Identity;
use actix_web::test;
use actix_web::test::call_service;
use actix_web::test::TestRequest;
use actix_web::HttpMessage;
use brass_macros::db_test;
use sqlx::{query, PgPool};

use crate::auth::utils::generate_salt_and_hash_plain_password;
use crate::auth::utils::hash_plain_password_with_salt;
use crate::endpoints::user::post_login::LoginForm;
use crate::models::Function;
use crate::models::Role;
use crate::models::User;
use crate::utils::test_helper::DbTestContext;

use super::Area;

use super::Result;

#[derive(Debug)]
pub struct  Location {
    pub id: i32,
    pub name: String,
    pub area_id: i32,
    pub area: Option<Area>,
}

impl Location {
    pub async fn create(pool: &PgPool, name: &str, area_id: i32) -> Result<()> {
        query!(
            "INSERT INTO location (name, areaId) VALUES ($1, $2);",
            name,
            area_id
        )
        .execute(pool)
        .await?;

        Ok(())
    }

    pub async fn read_by_area(pool: &PgPool, area_id: i32) -> Result<Vec<Location>> {
        let records = query!("SELECT * FROM location WHERE areaId = $1;", area_id)
            .fetch_all(pool)
            .await?;

        let locations = records
            .iter()
            .map(|lr| Location {
                id: lr.id,
                name: lr.name.to_string(),
                area_id: lr.areaid,
                area: None,
            })
            .collect();

        Ok(locations)
    }

    pub async fn read_all(pool: &PgPool) -> Result<Vec<Location>> {
        let records = query!("SELECT * FROM location").fetch_all(pool).await?;

        let locations = records
            .iter()
            .map(|lr| Location {
                id: lr.id,
                name: lr.name.to_string(),
                area_id: lr.areaid,
                area: None,
            })
            .collect();

        Ok(locations)
    }

    pub async fn read_all_including_area(pool: &PgPool) -> Result<Vec<Location>> {
        let records = query!("SELECT location.id AS locationId, location.name, location.areaId, area.id, area.name AS areaName FROM location JOIN area ON location.areaId = area.id;")
            .fetch_all(pool)
            .await?;

        let locations = records
            .iter()
            .map(|lr| Location {
                id: lr.locationid,
                name: lr.name.to_string(),
                area_id: lr.areaid,
                area: Some(Area {
                    id: lr.id,
                    name: lr.areaname.to_string(),
                }),
            })
            .collect();

        Ok(locations)
    }

    pub async fn read_by_id(pool: &PgPool, id: i32) -> Result<Option<Location>> {
        let record = query!("SELECT * FROM location WHERE id = $1;", id)
            .fetch_optional(pool)
            .await?;

        let location = record.and_then(|r| {
            Some(Location {
                id: r.id,
                name: r.name,
                area_id: r.areaid,
                area: None,
            })
        });

        Ok(location)
    }

    pub async fn update(pool: &PgPool, id: i32, name: &str, area_id: i32) -> Result<()> {
        query!(
            "UPDATE location SET name = $1, areaid = $2 WHERE id = $3;",
            name,
            area_id,
            id
        )
        .execute(pool)
        .await?;

        Ok(())
    }

    pub async fn delete(pool: &PgPool, id: i32) -> Result<()> {
        query!("DELETE FROM location WHERE id = $1;", id)
            .execute(pool)
            .await?;

        Ok(())
    }
}

#[db_test]
async fn test_read_all(context: &DbTestContext) {
    let app = context.app().await;

    let (hash, salt) = generate_salt_and_hash_plain_password("abc").unwrap();
    User::create_with_password(&context.db_pool, "abc", "abc", &hash, &salt, Role::Admin, Function::Wachhabender, 1).await.unwrap();

    Location::create(&context.db_pool, "wundervolle location", 1).await.unwrap();

    //let locations = Location::read_all(&context.db_pool).await.unwrap();
    //
    //assert_eq!(1, locations.len());

    let login_form = LoginForm {
        email: "abc".to_string(),
        password: "abc".to_string()
    };

    let login_req = TestRequest::post()
            .uri("/login")
            .set_form(login_form)
            .to_request();

    let login_resp = call_service(&app, login_req).await;
    //println!("{:?}", log);
    let cookie = login_resp.response().cookies().next().unwrap().to_owned();

    let req = TestRequest::get()
            .uri("/locations")
            .cookie(cookie)
            .to_request();

    //let http_req = TestRequest::get()
    //        .uri("/locations")
    //        .to_http_request();
    //let service_req = TestRequest::get()
    //        .uri("/locations")
    //        .to_srv_request();

    //Identity::login(&req.extensions(), "1".to_string()).unwrap();
    let resp = call_service(&context.app().await, req).await;
    //let resp = test::call_and_read_body(&context.app().await, http_req).await;

    println!("{:?}", resp.headers());
    assert_eq!(StatusCode::OK, resp.status());


    let body = test::read_body(resp).await;
    let body_string = String::from_utf8(body.to_vec()).unwrap();
    assert!(body_string.contains("wundervolle location"));
}
