package admin

import (
	"crussell/db"
	"crussell/internal/validators"
	"database/sql"
	"encoding/json"
	"log/slog"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"
)

// PatchTest represents a patch test definition
type PatchTest struct {
	ID                  string   `json:"id"`
	Name                string   `json:"name"`
	Description         *string  `json:"description,omitempty"`
	NoticeDurationHours int      `json:"notice_duration_hours"`
	ExpiryMonths        int      `json:"expiry_months"`
	ServiceIDs          []string `json:"service_ids"`
}

// CreatePatchTestRequest represents the request payload
type CreatePatchTestRequest struct {
	Name                string   `json:"name" validate:"required,min=1,max=200"`
	Description         *string  `json:"description,omitempty" validate:"omitempty,max=1000"`
	NoticeDurationHours int      `json:"notice_duration_hours"`
	ExpiryMonths        int      `json:"expiry_months"`
	ServiceIDs          []string `json:"service_ids"`
}

// UpdatePatchTestRequest represents the request payload
type UpdatePatchTestRequest struct {
	Name                *string  `json:"name,omitempty" validate:"omitempty,min=1,max=200"`
	Description         *string  `json:"description,omitempty" validate:"omitempty,max=1000"`
	NoticeDurationHours *int     `json:"notice_duration_hours,omitempty"`
	ExpiryMonths        *int     `json:"expiry_months,omitempty"`
	ServiceIDs          []string `json:"service_ids,omitempty"`
}

// GetPatchTests handles GET /api/admin/patch-tests
func GetPatchTests(w http.ResponseWriter, r *http.Request) {
	query := `
		SELECT id, name, description, notice_duration_hours, expiry_months, service_ids
		FROM patch_tests
		ORDER BY name
	`

	rows, err := db.Conn.Query(r.Context(), query)
	if err != nil {
		http.Error(w, "Failed to fetch patch tests: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var patchTests []PatchTest
	for rows.Next() {
		var pt PatchTest
		var desc sql.NullString
		err := rows.Scan(&pt.ID, &pt.Name, &desc, &pt.NoticeDurationHours, &pt.ExpiryMonths, &pt.ServiceIDs)
		if err != nil {
			http.Error(w, "Failed to read patch test data: "+err.Error(), http.StatusInternalServerError)
			return
		}
		if desc.Valid {
			pt.Description = &desc.String
		}
		patchTests = append(patchTests, pt)
	}
	if err := rows.Err(); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	_ = json.NewEncoder(w).Encode(patchTests)
}

// CreatePatchTest handles POST /api/admin/patch-tests
func CreatePatchTest(w http.ResponseWriter, r *http.Request) {
	var req CreatePatchTestRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	if err := validators.Validate.Struct(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := `
		INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids)
		VALUES ($1, $2, $3, $4, $5)
		RETURNING id
	`

	tx, err := db.Conn.Begin(r.Context())
	if err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	defer func() {
    if err := tx.Rollback(r.Context()); err != nil {
        slog.Error("failed to rollback transaction", "err", err)
    }
}()

	var id string
	err = tx.QueryRow(r.Context(), query, req.Name, req.Description, req.NoticeDurationHours, req.ExpiryMonths, req.ServiceIDs).Scan(&id)
	if err != nil {
		http.Error(w, "Failed to create patch test: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if err := tx.Commit(r.Context()); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusCreated)
	_ = json.NewEncoder(w).Encode(map[string]string{"id": id})
}

// UpdatePatchTest handles PUT /api/admin/patch-tests/{id}
func UpdatePatchTest(w http.ResponseWriter, r *http.Request) {
	id := chi.URLParam(r, "id")
	if id == "" || !validators.IsValidID(id) {
		http.Error(w, "Patch test not found", http.StatusNotFound)
		return
	}

	var req UpdatePatchTestRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	if err := validators.Validate.Struct(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := "UPDATE patch_tests SET "
	args := []any{}
	i := 1

	if req.Name != nil {
		query += "name = $" + strconv.Itoa(i) + ", "
		args = append(args, *req.Name)
		i++
	}
	if req.Description != nil {
		query += "description = $" + strconv.Itoa(i) + ", "
		args = append(args, *req.Description)
		i++
	}
	if req.NoticeDurationHours != nil {
		query += "notice_duration_hours = $" + strconv.Itoa(i) + ", "
		args = append(args, *req.NoticeDurationHours)
		i++
	}
	if req.ExpiryMonths != nil {
		query += "expiry_months = $" + strconv.Itoa(i) + ", "
		args = append(args, *req.ExpiryMonths)
		i++
	}
	if req.ServiceIDs != nil {
		query += "service_ids = $" + strconv.Itoa(i) + ", "
		args = append(args, req.ServiceIDs)
		i++
	}

	query = query[:len(query)-2]
	query += " WHERE id = $" + strconv.Itoa(i)
	args = append(args, id)

	tx, err := db.Conn.Begin(r.Context())
	if err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	defer func() {
    if err := tx.Rollback(r.Context()); err != nil {
        slog.Error("failed to rollback transaction", "err", err)
    }
}()

	_, err = tx.Exec(r.Context(), query, args...)
	if err != nil {
		http.Error(w, "Failed to update patch test", http.StatusInternalServerError)
		return
	}

	if err := tx.Commit(r.Context()); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// DeletePatchTest handles DELETE /api/admin/patch-tests/{id}
func DeletePatchTest(w http.ResponseWriter, r *http.Request) {
	id := chi.URLParam(r, "id")
	if id == "" || !validators.IsValidID(id) {
		http.Error(w, "Patch test not found", http.StatusNotFound)
		return
	}

	tx, err := db.Conn.Begin(r.Context())
	if err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	defer func() {
    if err := tx.Rollback(r.Context()); err != nil {
        slog.Error("failed to rollback transaction", "err", err)
    }
}()

	_, err = tx.Exec(r.Context(), "DELETE FROM patch_tests WHERE id = $1", id)
	if err != nil {
		http.Error(w, "Failed to delete patch test: "+err.Error(), http.StatusInternalServerError)
		return
	}

	if err := tx.Commit(r.Context()); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
