package handlers

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"

	"github.com/breakpilot/edu-search-service/internal/database"
	"github.com/breakpilot/edu-search-service/internal/publications"
	"github.com/breakpilot/edu-search-service/internal/staff"
)

// StaffHandlers handles staff-related API endpoints
type StaffHandlers struct {
	repo       *database.Repository
	crawler    *staff.StaffCrawler
	pubCrawler *publications.PublicationCrawler
}

// NewStaffHandlers creates new staff handlers
func NewStaffHandlers(repo *database.Repository, email string) *StaffHandlers {
	return &StaffHandlers{
		repo:       repo,
		crawler:    staff.NewStaffCrawler(repo),
		pubCrawler: publications.NewPublicationCrawler(repo, email),
	}
}

// SearchStaff searches for university staff
// GET /api/v1/staff/search?q=...&university_id=...&state=...&position_type=...&is_professor=...
func (h *StaffHandlers) SearchStaff(c *gin.Context) {
	params := database.StaffSearchParams{
		Query:  c.Query("q"),
		Limit:  parseIntDefault(c.Query("limit"), 20),
		Offset: parseIntDefault(c.Query("offset"), 0),
	}

	// Optional filters
	if uniID := c.Query("university_id"); uniID != "" {
		id, err := uuid.Parse(uniID)
		if err == nil {
			params.UniversityID = &id
		}
	}

	if deptID := c.Query("department_id"); deptID != "" {
		id, err := uuid.Parse(deptID)
		if err == nil {
			params.DepartmentID = &id
		}
	}

	if state := c.Query("state"); state != "" {
		params.State = &state
	}

	if uniType := c.Query("uni_type"); uniType != "" {
		params.UniType = &uniType
	}

	if posType := c.Query("position_type"); posType != "" {
		params.PositionType = &posType
	}

	if isProfStr := c.Query("is_professor"); isProfStr != "" {
		isProf := isProfStr == "true" || isProfStr == "1"
		params.IsProfessor = &isProf
	}

	result, err := h.repo.SearchStaff(c.Request.Context(), params)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, result)
}

// GetStaff gets a single staff member by ID
// GET /api/v1/staff/:id
func (h *StaffHandlers) GetStaff(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid staff ID"})
		return
	}

	staff, err := h.repo.GetStaff(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Staff not found"})
		return
	}

	c.JSON(http.StatusOK, staff)
}

// GetStaffPublications gets publications for a staff member
// GET /api/v1/staff/:id/publications
func (h *StaffHandlers) GetStaffPublications(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid staff ID"})
		return
	}

	pubs, err := h.repo.GetStaffPublications(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"publications": pubs,
		"total":        len(pubs),
		"staff_id":     id,
	})
}

// SearchPublications searches for publications
// GET /api/v1/publications/search?q=...&year=...&pub_type=...
func (h *StaffHandlers) SearchPublications(c *gin.Context) {
	params := database.PublicationSearchParams{
		Query:  c.Query("q"),
		Limit:  parseIntDefault(c.Query("limit"), 20),
		Offset: parseIntDefault(c.Query("offset"), 0),
	}

	if staffID := c.Query("staff_id"); staffID != "" {
		id, err := uuid.Parse(staffID)
		if err == nil {
			params.StaffID = &id
		}
	}

	if year := c.Query("year"); year != "" {
		y := parseIntDefault(year, 0)
		if y > 0 {
			params.Year = &y
		}
	}

	if yearFrom := c.Query("year_from"); yearFrom != "" {
		y := parseIntDefault(yearFrom, 0)
		if y > 0 {
			params.YearFrom = &y
		}
	}

	if yearTo := c.Query("year_to"); yearTo != "" {
		y := parseIntDefault(yearTo, 0)
		if y > 0 {
			params.YearTo = &y
		}
	}

	if pubType := c.Query("pub_type"); pubType != "" {
		params.PubType = &pubType
	}

	result, err := h.repo.SearchPublications(c.Request.Context(), params)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, result)
}

// GetStaffStats gets statistics about staff data
// GET /api/v1/staff/stats
func (h *StaffHandlers) GetStaffStats(c *gin.Context) {
	stats, err := h.repo.GetStaffStats(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, stats)
}

// ListUniversities lists all universities
// GET /api/v1/universities
func (h *StaffHandlers) ListUniversities(c *gin.Context) {
	universities, err := h.repo.ListUniversities(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"universities": universities,
		"total":        len(universities),
	})
}

// StartStaffCrawl starts a staff crawl for a university
// POST /api/v1/admin/crawl/staff
func (h *StaffHandlers) StartStaffCrawl(c *gin.Context) {
	var req struct {
		UniversityID string `json:"university_id"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
		return
	}

	uniID, err := uuid.Parse(req.UniversityID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university ID"})
		return
	}

	uni, err := h.repo.GetUniversity(c.Request.Context(), uniID)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "University not found"})
		return
	}

	// Start crawl in background
	go func() {
		result, err := h.crawler.CrawlUniversity(c.Request.Context(), uni)
		if err != nil {
			// Log error
			return
		}
		_ = result
	}()

	c.JSON(http.StatusAccepted, gin.H{
		"status":        "started",
		"university_id": uniID,
		"message":       "Staff crawl started in background",
	})
}

// StartPublicationCrawl starts a publication crawl for a university
// POST /api/v1/admin/crawl/publications
func (h *StaffHandlers) StartPublicationCrawl(c *gin.Context) {
	var req struct {
		UniversityID string `json:"university_id"`
		Limit        int    `json:"limit"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
		return
	}

	uniID, err := uuid.Parse(req.UniversityID)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university ID"})
		return
	}

	limit := req.Limit
	if limit <= 0 {
		limit = 50
	}

	// Start crawl in background
	go func() {
		status, err := h.pubCrawler.CrawlForUniversity(c.Request.Context(), uniID, limit)
		if err != nil {
			// Log error
			return
		}
		_ = status
	}()

	c.JSON(http.StatusAccepted, gin.H{
		"status":        "started",
		"university_id": uniID,
		"message":       "Publication crawl started in background",
	})
}

// ResolveDOI resolves a DOI and saves the publication
// POST /api/v1/publications/resolve-doi
func (h *StaffHandlers) ResolveDOI(c *gin.Context) {
	var req struct {
		DOI     string `json:"doi"`
		StaffID string `json:"staff_id,omitempty"`
	}

	if err := c.ShouldBindJSON(&req); err != nil || req.DOI == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "DOI is required"})
		return
	}

	pub, err := h.pubCrawler.ResolveDOI(c.Request.Context(), req.DOI)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Link to staff if provided
	if req.StaffID != "" {
		staffID, err := uuid.Parse(req.StaffID)
		if err == nil {
			link := &database.StaffPublication{
				StaffID:       staffID,
				PublicationID: pub.ID,
			}
			h.repo.LinkStaffPublication(c.Request.Context(), link)
		}
	}

	c.JSON(http.StatusOK, pub)
}

// GetCrawlStatus gets crawl status for a university
// GET /api/v1/admin/crawl/status/:university_id
func (h *StaffHandlers) GetCrawlStatus(c *gin.Context) {
	idStr := c.Param("university_id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid university ID"})
		return
	}

	status, err := h.repo.GetCrawlStatus(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if status == nil {
		c.JSON(http.StatusOK, gin.H{
			"university_id":      id,
			"staff_crawl_status": "never",
			"pub_crawl_status":   "never",
		})
		return
	}

	c.JSON(http.StatusOK, status)
}

// Helper to parse int with default
func parseIntDefault(s string, def int) int {
	if s == "" {
		return def
	}
	var n int
	_, err := fmt.Sscanf(s, "%d", &n)
	if err != nil {
		return def
	}
	return n
}

// RegisterStaffRoutes registers staff-related routes
func (h *StaffHandlers) RegisterRoutes(r *gin.RouterGroup) {
	// Public endpoints
	r.GET("/staff/search", h.SearchStaff)
	r.GET("/staff/stats", h.GetStaffStats)
	r.GET("/staff/:id", h.GetStaff)
	r.GET("/staff/:id/publications", h.GetStaffPublications)

	r.GET("/publications/search", h.SearchPublications)
	r.POST("/publications/resolve-doi", h.ResolveDOI)

	r.GET("/universities", h.ListUniversities)

	// Admin endpoints
	r.POST("/admin/crawl/staff", h.StartStaffCrawl)
	r.POST("/admin/crawl/publications", h.StartPublicationCrawl)
	r.GET("/admin/crawl/status/:university_id", h.GetCrawlStatus)
}
