package http

import (
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"strconv"

	"github.com/jobs-scraper/libs/ports"
	"github.com/jobs-scraper/libs/repo"
	"github.com/jobs-scraper/internal/pkg/utils"

	"github.com/gorilla/mux"
)

// JobHandler handles HTTP requests related to jobs
type JobHandler struct {
	jobCommands           *ports.JobCommands
	jobAnalysisResultRepo *repo.JobAnalysisResultRepository
}

// NewJobHandler creates a new instance of JobHandler
func NewJobHandler(jobCommands *ports.JobCommands, jobAnalysisResultRepo *repo.JobAnalysisResultRepository) *JobHandler {
	return &JobHandler{
		jobCommands:           jobCommands,
		jobAnalysisResultRepo: jobAnalysisResultRepo,
	}
}

// RegisterRoutes registers all routes to the router
func (h *JobHandler) RegisterRoutes(router *mux.Router) {
	jobs := router.PathPrefix("/jobs").Subrouter()

	//jobs.Use(AuthMiddleware)

	jobs.HandleFunc("", utils.Make(h.CreateJob)).Methods("POST")
	jobs.HandleFunc("/analysis", utils.Make(h.GetAllAnalysisResults)).Methods("GET")
	jobs.HandleFunc("/{id}/analysis", utils.Make(h.GetJobAnalysisResult)).Methods("GET")
	jobs.HandleFunc("/analysis/top-matches", utils.Make(h.GetTopMatches)).Methods("GET")
}

// CreateJob handles the creation of a new job
// @Summary Create a new job
// @Description Creates a new job with the provided specifications
// @Tags jobs
// @Accept json
// @Produce json
// @Param job body ports.CreateJobCommand true "Job creation data"
// @Success 201 {object} map[string]string "Successfully created job"
// @Failure 400 {object} map[string]string "Invalid request data"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs [post]
func (h *JobHandler) CreateJob(w http.ResponseWriter, r *http.Request) error {
	var cmd ports.CreateJobCommand
	if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil {
		slog.Error(err.Error())
		return utils.NewAPIError(http.StatusBadRequest, utils.InvalidJSON())
	}

	err := h.jobCommands.CreateJob.Handle(cmd)
	if err != nil {
		slog.Error(err.Error())
		return utils.NewAPIError(http.StatusInternalServerError, err)
	}

	utils.WriteJSON(w, http.StatusCreated, map[string]string{"message": "Success"})
	return nil
}

// GetAllAnalysisResults handles retrieving all job analysis results
// @Summary Get all job analysis results
// @Description Retrieves all job analysis results from the database
// @Tags jobs
// @Produce json
// @Success 200 {array} domain.JobAnalysisResult "List of job analysis results"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs/analysis [get]
func (h *JobHandler) GetAllAnalysisResults(w http.ResponseWriter, r *http.Request) error {
	results, err := h.jobAnalysisResultRepo.GetAllAnalysisResults()
	if err != nil {
		slog.Error("Failed to get analysis results", "error", err)
		return utils.NewAPIError(http.StatusInternalServerError, err)
	}

	utils.WriteJSON(w, http.StatusOK, results)
	return nil
}

// GetJobAnalysisResult handles retrieving analysis result for a specific job
// @Summary Get job analysis result by job ID
// @Description Retrieves the analysis result for a specific job
// @Tags jobs
// @Produce json
// @Param id path int true "Job ID"
// @Success 200 {object} domain.JobAnalysisResult "Job analysis result"
// @Failure 400 {object} map[string]string "Invalid job ID"
// @Failure 404 {object} map[string]string "Analysis result not found"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs/{id}/analysis [get]
func (h *JobHandler) GetJobAnalysisResult(w http.ResponseWriter, r *http.Request) error {
	vars := mux.Vars(r)
	jobID, err := strconv.ParseInt(vars["id"], 10, 64)
	if err != nil {
		return utils.NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid job ID"))
	}

	result, err := h.jobAnalysisResultRepo.GetAnalysisResultByJobID(jobID)
	if err != nil {
		slog.Error("Failed to get analysis result", "jobID", jobID, "error", err)
		return utils.NewAPIError(http.StatusNotFound, fmt.Errorf("analysis result not found"))
	}

	utils.WriteJSON(w, http.StatusOK, result)
	return nil
}

// GetTopMatches handles retrieving top matching jobs based on analysis score
// @Summary Get top matching jobs
// @Description Retrieves jobs with high analysis match scores
// @Tags jobs
// @Produce json
// @Param min_score query int false "Minimum match score (default: 70)"
// @Success 200 {array} domain.JobAnalysisResult "List of top matching job analysis results"
// @Failure 400 {object} map[string]string "Invalid minimum score"
// @Failure 500 {object} map[string]string "Internal server error"
// @Router /jobs/analysis/top-matches [get]
func (h *JobHandler) GetTopMatches(w http.ResponseWriter, r *http.Request) error {
	minScoreStr := r.URL.Query().Get("min_score")
	minScore := 70 // default minimum score
	
	if minScoreStr != "" {
		var err error
		minScore, err = strconv.Atoi(minScoreStr)
		if err != nil {
			return utils.NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid minimum score"))
		}
	}

	results, err := h.jobAnalysisResultRepo.GetAnalysisResultsByMatchScore(minScore)
	if err != nil {
		slog.Error("Failed to get top matches", "minScore", minScore, "error", err)
		return utils.NewAPIError(http.StatusInternalServerError, err)
	}

	utils.WriteJSON(w, http.StatusOK, results)
	return nil
}
