use log::{info};
use std::env;
use std::fs;

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, middleware::Logger};
use actix_web::error::HttpError;
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Movie {
    title: String,
    director: String,
    release_year: u16,
}

fn get_storage_file() -> String {
    let storage_dir: String = match env::var("STORAGE_DIR") {
	Ok(storage_dir) => storage_dir,
	Err(_) => panic!("You must set STORAGE_DIR env var")
    };

    // fs::create_dir_all(storage_dir).expect("unable to create storage directory");
    
    match storage_dir.chars().last().unwrap() {
	'/' => storage_dir + "storage.json",
	_ => storage_dir + "/storage.json",
    }
}

fn get_all_from_storage() -> anyhow::Result<Vec<Movie>> {
    let raw_data = fs::read_to_string(get_storage_file())?;
    let movies: Vec<Movie> = serde_json::from_str(&raw_data)?;
    Ok(movies)
}

fn write_to_storage(movies: Vec<Movie>) -> anyhow::Result<()> {    
    let data = serde_json::to_string_pretty(&movies).expect("Failed to serialize movies.");
    fs::write(get_storage_file(), data).expect("Unable to write file");
    Ok(())
}

#[get("/")]
async fn get_all() -> Result<impl Responder, HttpError> {
    match get_all_from_storage() {
        Ok(movies) => return Ok(HttpResponse::Ok().json(movies)),
        Err(_) => return Ok(HttpResponse::InternalServerError().body("storage file not found at 'storage/storage.json'"))
    }

}

#[get("/{title}")]
async fn get_one(title: web::Path<String>) -> Result<impl Responder, HttpError> {
    return match get_all_from_storage() {
        Ok(movies) => {
            for movie in movies {
                if movie.title.to_lowercase().replace(' ', "") == title.to_string().to_lowercase().replace(' ', "") {
                    return Ok(HttpResponse::Ok().json(movie));
                }
            }

            Ok(HttpResponse::NotFound().body(format!("Movie '{}' not found", title)))
        },
        Err(_) => return Ok(HttpResponse::InternalServerError().body("storage file not found at 'storage/storage.json'"))
    }
}

#[post("/")]
async fn insert(new_movie: web::Json<Movie>) -> Result<impl Responder, HttpError> {
    let mut movies: Vec<Movie>;
    match get_all_from_storage() {
        Ok(_movies) => movies = _movies,
        Err(_) => movies = vec!()
    };

    for movie in &movies {
        if movie.title.to_lowercase().replace(' ', "") == new_movie.title.to_string().to_lowercase().replace(' ', "") {
            return Ok(HttpResponse::BadRequest().body(format!("Movie '{}' already exists", new_movie.title)))
        }
    }

    movies.push(new_movie.into_inner());

    write_to_storage(movies);

    Ok(HttpResponse::Created().body("Created"))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));

    let port: u16 = match env::var("PORT") {
	Ok(port) => port.parse().unwrap(),
	Err(_) => panic!("You must set PORT env var")
    };

    info!("App started on port {}", port);
   
    HttpServer::new(|| {
        App::new()
	    .wrap(Logger::new("%a \"%r\" %s b  %T"))
            .service(get_all)
            .service(get_one)
            .service(insert)
    })
    .bind(("0.0.0.0", port))?
    .run()
    .await
}
