// retrigger-build-marker-2
#![allow(dead_code)]

use axum::{
    extract::State,
    http::{HeaderValue, Method, StatusCode},
    routing::get,
    Json, Router,
};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use std::net::SocketAddr;
use std::sync::Arc;
use tower_http::cors::{AllowHeaders, AllowOrigin, CorsLayer};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

#[derive(Clone)]
pub struct AppState {
    pub pool: PgPool,
}

#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
pub struct Job {
    pub id: uuid::Uuid,
    pub title: String,
    pub description: String,
    pub location: String,
    pub job_type: String,
    pub status: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Deserialize)]
pub struct CreateJob {
    pub title: String,
    pub description: String,
    pub location: String,
    pub job_type: String,
}

async fn list_jobs(State(state): State<Arc<AppState>>) -> Result<Json<Vec<Job>>, StatusCode> {
    let jobs = sqlx::query_as::<_, Job>(
        "SELECT id, title, description, location, job_type, status, created_at FROM jobs ORDER BY created_at DESC"
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(Json(jobs))
}

async fn create_job(
    State(state): State<Arc<AppState>>,
    Json(payload): Json<CreateJob>,
) -> Result<Json<Job>, StatusCode> {
    let job = sqlx::query_as::<_, Job>(
        r#"
        INSERT INTO jobs (title, description, location, job_type)
        VALUES ($1, $2, $3, $4)
        RETURNING id, title, description, location, job_type, status, created_at
        "#,
    )
    .bind(&payload.title)
    .bind(&payload.description)
    .bind(&payload.location)
    .bind(&payload.job_type)
    .fetch_one(&state.pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(Json(job))
}

async fn get_job(
    State(state): State<Arc<AppState>>,
    axum::extract::Path(id): axum::extract::Path<uuid::Uuid>,
) -> Result<Json<Job>, StatusCode> {
    let job = sqlx::query_as::<_, Job>(
        "SELECT id, title, description, location, job_type, status, created_at FROM jobs WHERE id = $1"
    )
    .bind(id)
    .fetch_optional(&state.pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
    .ok_or(StatusCode::NOT_FOUND)?;

    Ok(Json(job))
}

async fn health() -> &'static str {
    "Jobs Service OK"
}

fn build_cors() -> CorsLayer {
    let frontend_url = std::env::var("FRONTEND_URL")
        .expect("FRONTEND_URL must be set");
    let admin_url = std::env::var("ADMIN_URL")
        .expect("ADMIN_URL must be set");

    let allowed_origins: Vec<HeaderValue> = vec![
        frontend_url.parse().expect("Invalid FRONTEND_URL"),
        admin_url.parse().expect("Invalid ADMIN_URL"),
    ];

    CorsLayer::new()
        .allow_origin(AllowOrigin::list(allowed_origins))
        .allow_methods([
            Method::GET,
            Method::POST,
            Method::PUT,
            Method::PATCH,
            Method::DELETE,
            Method::OPTIONS,
        ])
        .allow_headers(AllowHeaders::mirror_request())
        .allow_credentials(true)
}

#[tokio::main]
async fn main() {
    tracing_subscriber::registry()
        .with(tracing_subscriber::EnvFilter::new(
            std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()),
        ))
        .with(tracing_subscriber::fmt::layer())
        .init();

    let database_url = std::env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");

    let pool = sqlx::postgres::PgPoolOptions::new()
        .max_connections(10)
        .connect(&database_url)
        .await
        .expect("Failed to connect to database");

    tracing::info!("Connected to database");

    let state = Arc::new(AppState { pool });

    let cors = build_cors();

    let app = Router::new()
        .route("/health", get(health))
        .nest(
            "/api/jobs",
            Router::new()
                .route("/", get(list_jobs).post(create_job))
                .route("/{id}", get(get_job)),
        )
        .layer(cors)
        .with_state(state);

    let port: u16 = std::env::var("PORT")
        .unwrap_or_else(|_| "9103".to_string())
        .parse()
        .expect("PORT must be a valid u16");
    let addr = SocketAddr::from(([0, 0, 0, 0], port));

    tracing::info!("Jobs service listening on {}", addr);

    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
