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 }