// REST API Demo Project
// A simple in-memory task management API demonstrating:
// - HttpServer effect for handling requests
// - Pattern matching for routing
// - JSON building
// - Effect tracking for all side effects
//
// Run with: lux projects/rest-api/main.lux
// Test with:
//   curl http://localhost:8080/tasks
//   curl http://localhost:8080/tasks/1

// ============================================================
// Data Types
// ============================================================

// Task type is: { id: Int, title: String, done: Bool }

// API response wrapper
type ApiResponse =
    | Success(String)
    | NotFound(String)
    | BadRequest(String)
    | MethodNotAllowed(String)

// ============================================================
// JSON Helpers
// ============================================================

// Build a JSON object string from a task
fn taskToJson(task: { id: Int, title: String, done: Bool }): String = {
    let doneStr = if task.done then "true" else "false"
    let q = "\""
    "\{" + q + "id" + q + ":" + toString(task.id) + "," + q + "title" + q + ":" + q + task.title + q + "," + q + "done" + q + ":" + doneStr + "\}"
}

fn tasksToJson(tasks: List<{ id: Int, title: String, done: Bool }>): String = {
    let items = List.map(tasks, fn(t: { id: Int, title: String, done: Bool }): String => taskToJson(t))
    "[" + String.join(items, ",") + "]"
}

fn errorJson(msg: String): String = {
    let q = "\""
    "\{" + q + "error" + q + ":" + q + msg + q + "\}"
}

fn messageJson(msg: String, version: String): String = {
    let q = "\""
    "\{" + q + "message" + q + ":" + q + msg + q + "," + q + "version" + q + ":" + q + version + q + "\}"
}

fn deletedJson(): String = {
    let q = "\""
    "\{" + q + "deleted" + q + ":true\}"
}

// ============================================================
// Request Parsing
// ============================================================

fn extractId(path: String): Option<Int> = {
    // Extract ID from path like "/tasks/123"
    let parts = String.split(path, "/")
    match List.get(parts, 2) {
        Some(idStr) => {
            match idStr {
                "1" => Some(1),
                "2" => Some(2),
                "3" => Some(3),
                "4" => Some(4),
                "5" => Some(5),
                _ => None
            }
        },
        None => None
    }
}

// ============================================================
// Route Handlers
// ============================================================

fn handleGetTasks(): ApiResponse = {
    let task1 = { id: 1, title: "Learn Lux", done: true }
    let task2 = { id: 2, title: "Build API", done: false }
    let task3 = { id: 3, title: "Deploy app", done: false }
    let tasks = [task1, task2, task3]
    Success(tasksToJson(tasks))
}

fn handleGetTask(id: Int): ApiResponse = {
    match id {
        1 => Success(taskToJson({ id: 1, title: "Learn Lux", done: true })),
        2 => Success(taskToJson({ id: 2, title: "Build API", done: false })),
        3 => Success(taskToJson({ id: 3, title: "Deploy app", done: false })),
        _ => NotFound(errorJson("Task not found"))
    }
}

fn handleCreateTask(body: String): ApiResponse = {
    let newTask = { id: 4, title: "New Task", done: false }
    Success(taskToJson(newTask))
}

fn handleUpdateTask(id: Int, body: String): ApiResponse = {
    match id {
        1 => Success(taskToJson({ id: 1, title: "Learn Lux", done: true })),
        2 => Success(taskToJson({ id: 2, title: "Build API", done: true })),
        3 => Success(taskToJson({ id: 3, title: "Deploy app", done: true })),
        _ => NotFound(errorJson("Task not found"))
    }
}

fn handleDeleteTask(id: Int): ApiResponse = {
    match id {
        1 => Success(deletedJson()),
        2 => Success(deletedJson()),
        3 => Success(deletedJson()),
        _ => NotFound(errorJson("Task not found"))
    }
}

// ============================================================
// Router
// ============================================================

fn route(method: String, path: String, body: String): ApiResponse = {
    if method == "GET" then {
        if path == "/tasks" then handleGetTasks()
        else if path == "/" then Success(messageJson("Lux REST API", "1.0"))
        else if String.contains(path, "/tasks/") then {
            match extractId(path) {
                Some(id) => handleGetTask(id),
                None => BadRequest(errorJson("Invalid task ID"))
            }
        }
        else NotFound(errorJson("Not found"))
    }
    else if method == "POST" then {
        if path == "/tasks" then handleCreateTask(body)
        else NotFound(errorJson("Not found"))
    }
    else if method == "PUT" then {
        if String.contains(path, "/tasks/") then {
            match extractId(path) {
                Some(id) => handleUpdateTask(id, body),
                None => BadRequest(errorJson("Invalid task ID"))
            }
        }
        else NotFound(errorJson("Not found"))
    }
    else if method == "DELETE" then {
        if String.contains(path, "/tasks/") then {
            match extractId(path) {
                Some(id) => handleDeleteTask(id),
                None => BadRequest(errorJson("Invalid task ID"))
            }
        }
        else NotFound(errorJson("Not found"))
    }
    else MethodNotAllowed(errorJson("Method not allowed"))
}

// ============================================================
// Request Handler
// ============================================================

fn handleRequest(req: { method: String, path: String, body: String, headers: List<(String, String)> }): Unit with {Console, HttpServer} = {
    Console.print(req.method + " " + req.path)
    let response = route(req.method, req.path, req.body)
    let contentType = [("Content-Type", "application/json")]
    match response {
        Success(json) => HttpServer.respondWithHeaders(200, json, contentType),
        NotFound(json) => HttpServer.respondWithHeaders(404, json, contentType),
        BadRequest(json) => HttpServer.respondWithHeaders(400, json, contentType),
        MethodNotAllowed(json) => HttpServer.respondWithHeaders(405, json, contentType)
    }
}

// ============================================================
// Server Loop
// ============================================================

fn serveRequests(count: Int, maxRequests: Int): Unit with {Console, HttpServer} = {
    if count >= maxRequests then {
        Console.print("Max requests reached, shutting down...")
        HttpServer.stop()
    } else {
        let req = HttpServer.accept()
        handleRequest(req)
        serveRequests(count + 1, maxRequests)
    }
}

// ============================================================
// Main Entry Point
// ============================================================

fn main(): Unit with {Console, HttpServer} = {
    let port = 8080
    let maxRequests = 10
    Console.print("========================================")
    Console.print("  Lux REST API Demo")
    Console.print("========================================")
    Console.print("")
    Console.print("Starting server on port " + toString(port) + "...")
    Console.print("Will handle " + toString(maxRequests) + " requests then stop.")
    Console.print("")
    Console.print("Endpoints:")
    Console.print("  GET    /           - API info")
    Console.print("  GET    /tasks      - List all tasks")
    Console.print("  GET    /tasks/:id  - Get task by ID")
    Console.print("  POST   /tasks      - Create task")
    Console.print("  PUT    /tasks/:id  - Update task")
    Console.print("  DELETE /tasks/:id  - Delete task")
    Console.print("")
    Console.print("Try:")
    Console.print("  curl http://localhost:8080/")
    Console.print("  curl http://localhost:8080/tasks")
    Console.print("  curl http://localhost:8080/tasks/1")
    Console.print("")
    HttpServer.listen(port)
    Console.print("Server listening!")
    Console.print("")
    serveRequests(0, maxRequests)
}

let output = run main() with {}
