mod create_db;
use create_db::init;

use actix_web::{App, web, HttpServer, get, Responder, HttpResponse, http::header::ContentType};
use actix_files::Files;
use serde_json::json;
use sqlite::{Connection, State}; 
use serde::{Serialize, Deserialize};

#[derive(serde::Serialize)]
struct Article {
    id: i64,
    title: String,
    auteur: String,
    edited_at: String,
    published_at: String,
    content: String,
}

#[get("/api/hello")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[get("/api/articles")]
async fn get_articles() -> impl Responder {
    let conn = Connection::open("./data/data.db").unwrap();
    let mut stmt = conn.prepare("SELECT * FROM articles").unwrap();
    let mut articles = Vec::new();

    while let State::Row = stmt.next().unwrap() {
        let id = stmt.read::<i64, _>(0).unwrap();
        let title = stmt.read::<String, _>(1).unwrap();
        let content = stmt.read::<String, _>(5).unwrap();
        articles.push(Article {
            id,
            title,
            auteur: "".to_string(),
            edited_at: "".to_string(),
            published_at: "".to_string(),
            content
        });
    }

    HttpResponse::Ok().json(articles)
}


#[get("/api/articles/{id}")]
async fn get_article(path: web::Path<i64>) -> impl Responder {
    let id = path.into_inner();

    // Open the database connection
    let conn = match Connection::open("./data/data.db") {
        Ok(conn) => conn,
        Err(err) => {
            eprintln!("Failed to connect to database: {}", err);
            return HttpResponse::InternalServerError().body("Failed to connect to database");
        }
    };

    // Fetch the article from the database
    match fetch_article_by_id(&conn, id) {
        Ok(Some(article)) => HttpResponse::Ok().json(article),
        Ok(None) => HttpResponse::NotFound().body(format!("Article with ID {} not found", id)),
        Err(err) => {
            eprintln!("Database query error: {}", err);
            HttpResponse::InternalServerError().body("Database query failed")
        }
    }
}

/// Fetches an article by its ID from the database.
fn fetch_article_by_id(conn: &Connection, id: i64) -> Result<Option<Article>, sqlite::Error> {
    let mut stmt = conn.prepare(
        "SELECT id, title, auteur, edited_at, published_at, content 
         FROM articles WHERE id = ?1"
    )?;
    stmt.bind((1, id))?;

    let mut article = None;
    while let State::Row = stmt.next()? {
        article = Some(Article {
            id: stmt.read::<i64, _>(0)?,
            title: stmt.read::<String, _>(1)?,
            auteur: stmt.read::<String, _>(2)?,
            edited_at: stmt.read::<String, _>(3)?,
            published_at: stmt.read::<String, _>(4)?,
            content: stmt.read::<String, _>(5)?,
        });
    }

    Ok(article)
}

#[get("/api")]
async fn api() -> impl Responder {
    let value = json!({
        "code": 200,
        "success": true,
        "payload": {
            "features": [
                "serde",
                "json"
            ],
            "homepage": null
        }
    });
    HttpResponse::Ok()
        .content_type(ContentType::json())
        .body(value.to_string())
}

#[actix_web::main]
async fn main() -> Result<(), std::io::Error> {
    let _ = init();

    HttpServer::new(|| {
        App::new()
            .service(hello)
            .service(get_articles)
            .service(api)
            .service(get_article)
            .service(Files::new("/", "public").index_file("index.html"))
    })
    .bind(("0.0.0.0", 2486))?
    .run()
    .await
}

