mod git_tracker;
mod post_manager;
mod template_engine;

use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::{Html, IntoResponse, Response},
    routing::get,
    Router,
};
use clap::Parser;
use std::sync::Arc;
use tokio::process::Command;
use tokio::sync::RwLock;
use tower_http::services::ServeDir;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    /// Disable caching of markdown rendering
    #[arg(long)]
    no_cache: bool,
}

#[derive(Clone)]
struct AppState {
    post_manager: Arc<RwLock<post_manager::PostManager>>,
}

#[tokio::main]
async fn main() {
    let args = Args::parse();

    let post_manager = Arc::new(RwLock::new(
        post_manager::PostManager::new(".", args.no_cache).expect("Failed to initialize post manager"),
    ));

    let app_state = AppState {
        post_manager: post_manager.clone(),
    };

    if args.no_cache {
        println!("Running with caching disabled");
    }

    let p_router = Router::new()
        .route("/:post_name", get(post_handler))
        .fallback_service(ServeDir::new("posts"));

    let app = Router::new()
        .route("/", get(index_handler))
        .route("/update", get(update_handler))
        .route("/:page", get(all_handler))
        .nest("/p", p_router)
        .with_state(app_state);

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .expect("Failed to bind to address");

    println!("Server running on http://127.0.0.1:3000");

    axum::serve(listener, app)
        .await
        .expect("Failed to start server");
}

async fn index_handler(State(state): State<AppState>) -> Response {
    match render_template("index.html", &state).await {
        Ok(html) => Html(html).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(),
    }
}

async fn all_handler(State(state): State<AppState>, Path(page): Path<String>) -> Response {
    if page.contains("..") {
        return (StatusCode::NOT_FOUND, "Invalid path").into_response();
    }
    match render_template(&format!("page_{}.html", page), &state).await {
        Ok(html) => Html(html).into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(),
    }
}

async fn post_handler(
    State(state): State<AppState>,
    Path(post_name): Path<String>,
) -> Response {
    if post_name.contains("..") {
        return (StatusCode::NOT_FOUND, "Invalid path").into_response();
    }

    let manager = state.post_manager.read().await;
    match manager.get_post(&post_name).await {
        Some(_post) => {
            drop(manager);
            match render_post_template(&state, post_name).await {
                Ok(html) => Html(html).into_response(),
                Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(),
            }
        }
        None => (StatusCode::NOT_FOUND, "Post not found").into_response(),
    }
}

async fn update_handler(State(state): State<AppState>) -> impl IntoResponse {
    // Run git pull --autostash
    let output = Command::new("git")
        .arg("pull")
        .arg("--autostash")
        .output()
        .await;

    match output {
        Ok(output) => {
            if !output.status.success() {
                let stderr = String::from_utf8_lossy(&output.stderr);
                eprintln!("Git pull failed: {}", stderr);
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    format!("Git pull failed: {}", stderr),
                )
                    .into_response();
            }

            let stdout = String::from_utf8_lossy(&output.stdout);
            println!("Git pull output: {}", stdout);

            // Refresh posts
            let mut manager = state.post_manager.write().await;
            match manager.refresh_posts() {
                Ok(_) => {
                    println!("Successfully refreshed log pages");
                    (StatusCode::OK, "Update successful: pulled changes and refreshed log pages")
                        .into_response()
                }
                Err(e) => {
                    eprintln!("Failed to refresh posts: {}", e);
                    (
                        StatusCode::INTERNAL_SERVER_ERROR,
                        format!("Failed to refresh posts: {}", e),
                    )
                        .into_response()
                }
            }
        }
        Err(e) => {
            eprintln!("Failed to execute git pull: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Failed to execute git pull: {}", e),
            )
                .into_response()
        }
    }
}


async fn render_template(template_name: &str, state: &AppState) -> Result<String, String> {
    let template_path = format!("templates/{}", template_name);
    let template_content = std::fs::read_to_string(&template_path)
        .map_err(|e| format!("Failed to read template: {}", e))?;

    let manager = state.post_manager.read().await;
    template_engine::render_template(&template_content, &*manager, None).await
}

async fn render_post_template(state: &AppState, post_name: String) -> Result<String, String> {
    let template_content = std::fs::read_to_string("templates/post.html")
        .map_err(|e| format!("Failed to read post template: {}", e))?;

    let manager = state.post_manager.read().await;
    template_engine::render_template(&template_content, &*manager, Some(&post_name)).await
}
