package handlers

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type Document struct {
	ID        int       `json:"id"`
	Title     string    `json:"title"`
	Content   string    `json:"content"`
	Author    string    `json:"author"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func DocumentsHandler(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")

        parts := strings.Split(strings.TrimSuffix(r.URL.Path, "/"), "/")
        idStr := parts[len(parts)-1]
        hasID := idStr != "documents" && idStr != ""

		switch {
		case r.Method == http.MethodGet && !hasID:
			listDocuments(w, r, db)
		case r.Method == http.MethodPost && !hasID:
			createDocument(w, r, db)
		case r.Method == http.MethodPut && hasID:
			id, err := strconv.Atoi(idStr)
			if err != nil {
				http.Error(w, "invalid id", http.StatusBadRequest)
				return
			}
			updateDocument(w, r, db, id)
		case r.Method == http.MethodDelete && hasID:
			id, err := strconv.Atoi(idStr)
			if err != nil {
				http.Error(w, "invalid id", http.StatusBadRequest)
				return
    }
    deleteDocument(w, r, db, id)
		default:
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		}
	}
}

func listDocuments(w http.ResponseWriter, _ *http.Request, db *sql.DB) {
	rows, err := db.Query(`SELECT id, title, content, COALESCE(author,''), created_at, updated_at FROM documents ORDER BY updated_at DESC`)
	if err != nil {
		http.Error(w, "db error", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	docs := []Document{}
	for rows.Next() {
		var d Document
		if err := rows.Scan(&d.ID, &d.Title, &d.Content, &d.Author, &d.CreatedAt, &d.UpdatedAt); err != nil {
			http.Error(w, "scan error", http.StatusInternalServerError)
			return
		}
		docs = append(docs, d)
	}
	json.NewEncoder(w).Encode(docs)
}

func createDocument(w http.ResponseWriter, r *http.Request, db *sql.DB) {
	var req struct {
		Title   string `json:"title"`
		Content string `json:"content"`
		Author  string `json:"author"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}
	if req.Title == "" || req.Content == "" {
		http.Error(w, "title and content required", http.StatusBadRequest)
		return
	}

	var d Document
	err := db.QueryRow(
		`INSERT INTO documents (title, content, author) VALUES ($1, $2, $3)
		 RETURNING id, title, content, COALESCE(author,''), created_at, updated_at`,
		req.Title, req.Content, req.Author,
	).Scan(&d.ID, &d.Title, &d.Content, &d.Author, &d.CreatedAt, &d.UpdatedAt)
	if err != nil {
		http.Error(w, "db error", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(d)
}

func updateDocument(w http.ResponseWriter, r *http.Request, db *sql.DB, id int) {
	var req struct {
		Title   string `json:"title"`
		Content string `json:"content"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}
	if req.Title == "" || req.Content == "" {
		http.Error(w, "title and content required", http.StatusBadRequest)
		return
	}

	var d Document
	err := db.QueryRow(
		`UPDATE documents SET title=$1, content=$2, updated_at=NOW()
		 WHERE id=$3
		 RETURNING id, title, content, COALESCE(author,''), created_at, updated_at`,
		req.Title, req.Content, id,
	).Scan(&d.ID, &d.Title, &d.Content, &d.Author, &d.CreatedAt, &d.UpdatedAt)
	if err == sql.ErrNoRows {
		http.Error(w, "document not found", http.StatusNotFound)
		return
	}
	if err != nil {
		http.Error(w, "db error", http.StatusInternalServerError)
		return
	}

	json.NewEncoder(w).Encode(d)
}

func deleteDocument(w http.ResponseWriter, _ *http.Request, db *sql.DB, id int) {
    result, err := db.Exec(`DELETE FROM documents WHERE id=$1`, id)
    if err != nil {
        http.Error(w, "db error", http.StatusInternalServerError)
        return
    }
    rows, _ := result.RowsAffected()
    if rows == 0 {
        http.Error(w, "document not found", http.StatusNotFound)
        return
    }
    w.WriteHeader(http.StatusNoContent)
}