package handlers

import (
	"bookhoard/internal/database"
	wsync "bookhoard/internal/sync"
	"bookhoard/internal/utils"
	"context"
	"errors"
	"net/http"
	"strconv"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgtype"
	"github.com/labstack/echo/v5"
)

// getDeviceIcon returns an emoji icon for device type
func getDeviceIcon(deviceType string) string {
	switch deviceType {
	case "kobo":
		return "📚"
	case "koreader":
		return "📖"
	case "kindle":
		return "📱"
	default:
		return "📚"
	}
}

// GetUniversalProgress retrieves progress with all location references
func (h *Handler) GetUniversalProgress(c *echo.Context) error {
	user := MustGetAuthenticatedUser(c)

	mediaItemID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid media item ID")
	}

	progress, err := h.db.GetUniversalProgress(context.Background(), database.GetUniversalProgressParams{
		MediaItemID: pgtype.UUID{Bytes: mediaItemID, Valid: true},
		UserID:      pgtype.UUID{Bytes: user.ID.Bytes, Valid: true},
	})
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return c.JSON(http.StatusOK, map[string]interface{}{
				"media_item_id": mediaItemID,
				"progress":      nil,
			})
		}
		return echo.NewHTTPError(http.StatusInternalServerError, "failed to get progress")
	}

	response := map[string]interface{}{
		"book_id":      progress.MediaItemID.Bytes,
		"format_group": progress.FormatGroup,
		"universal_progress": map[string]interface{}{
			"percentage": progress.Percentage.Float64,
		},
		"location_references": map[string]interface{}{
			"percentage":  progress.Percentage.Float64,
			"page":        int(progress.CurrentPage.Int32),
			"total_pages": int(progress.TotalPages.Int32),
		},
	}

	if progress.Epubcfi.Valid {
		response["location_references"].(map[string]interface{})["epubcfi"] = progress.Epubcfi.String
	}
	if progress.Chapter.Valid {
		response["location_references"].(map[string]interface{})["chapter"] = int(progress.Chapter.Int32)
	}
	if progress.ChapterProgress.Valid {
		response["location_references"].(map[string]interface{})["chapter_progress"] = progress.ChapterProgress.Float64
	}
	if progress.CharacterOffset.Valid {
		response["location_references"].(map[string]interface{})["character"] = progress.CharacterOffset.Int64
	}

	deviceSync := map[string]interface{}{}
	if progress.LastSyncDevice.Valid {
		deviceSync["device"] = progress.LastSyncDevice.String
	}
	if progress.LastSyncSource.Valid {
		deviceSync["source"] = progress.LastSyncSource.String
	}
	if progress.LastSyncTimestamp.Valid {
		deviceSync["last_sync"] = progress.LastSyncTimestamp.Time
	}
	if len(deviceSync) > 0 {
		response["device_sync"] = deviceSync
	}

	return c.JSON(http.StatusOK, response)
}

// UpdateUniversalProgress updates progress with automatic conversion
func (h *Handler) UpdateUniversalProgress(c *echo.Context) error {
	user := MustGetAuthenticatedUser(c)

	mediaItemID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid media item ID")
	}

	var req struct {
		Source   string `json:"source"`
		Location struct {
			Percentage *float64 `json:"percentage"`
			Page       *int     `json:"page"`
			TotalPages *int     `json:"total_pages"`
			Epubcfi    *string  `json:"epubcfi"`
			Character  *int64   `json:"character"`
			Chapter    *int     `json:"chapter"`
			ViewportY  *float64 `json:"viewport_y"`
		} `json:"location"`
		DeviceMetadata struct {
			DeviceType string `json:"device_type"`
			UserAgent  string `json:"user_agent"`
		} `json:"device_metadata"`
	}

	if err := c.Bind(&req); err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid request body")
	}

	mediaItem, err := h.db.GetMediaItem(context.Background(), pgtype.UUID{Bytes: mediaItemID, Valid: true})
	if err != nil {
		return echo.NewHTTPError(http.StatusNotFound, "media item not found")
	}

	percentage := 0.0
	if req.Location.Percentage != nil {
		percentage = *req.Location.Percentage
	} else if req.Location.Page != nil && req.Location.TotalPages != nil {
		percentage = wsync.PageToPercentage(*req.Location.Page, *req.Location.TotalPages)
	}

	currentPage := 0
	totalPages := 0
	if req.Location.Page != nil {
		currentPage = *req.Location.Page
	}
	if req.Location.TotalPages != nil {
		totalPages = *req.Location.TotalPages
	}
	if mediaItem.PageCount.Valid && mediaItem.PageCount.Int32 > 0 {
		totalPages = int(mediaItem.PageCount.Int32)
	}

	var epubcfi pgtype.Text
	if req.Location.Epubcfi != nil {
		epubcfi = pgtype.Text{String: *req.Location.Epubcfi, Valid: true}
	}

	var chapter pgtype.Int4
	if req.Location.Chapter != nil {
		chapter = pgtype.Int4{Int32: int32(*req.Location.Chapter), Valid: true}
	}

	var viewportY pgtype.Float8
	if req.Location.ViewportY != nil {
		viewportY = pgtype.Float8{Float64: *req.Location.ViewportY, Valid: true}
	}

	var lastSyncDevice, lastSyncSource pgtype.Text
	if req.DeviceMetadata.DeviceType != "" {
		lastSyncDevice = pgtype.Text{String: req.DeviceMetadata.DeviceType, Valid: true}
		lastSyncSource = pgtype.Text{String: req.Source, Valid: true}
	}

	updated, err := h.db.UpdateUniversalProgress(context.Background(), database.UpdateUniversalProgressParams{
		MediaItemID:     pgtype.UUID{Bytes: mediaItemID, Valid: true},
		UserID:          pgtype.UUID{Bytes: user.ID.Bytes, Valid: true},
		Percentage:      pgtype.Float8{Float64: percentage, Valid: true},
		Epubcfi:         epubcfi,
		Chapter:         chapter,
		ChapterProgress: pgtype.Float8{Float64: percentage, Valid: true},
		ViewportY:       viewportY,
		LastSyncDevice:  lastSyncDevice,
		LastSyncSource:  lastSyncSource,
		CurrentPage:     pgtype.Int4{Int32: int32(currentPage), Valid: true},
		TotalPages:      pgtype.Int4{Int32: int32(totalPages), Valid: true},
	})
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "failed to update progress")
	}

	// Broadcast progress update to all connected WebSocket clients
	deviceName := "Web"
	deviceType := "web"
	if req.DeviceMetadata.DeviceType != "" {
		deviceType = req.DeviceMetadata.DeviceType
		deviceName = req.DeviceMetadata.DeviceType
	}

	h.connManager.BroadcastProgressUpdate(mediaItemID, percentage, wsync.SourceDevice{
		ID:   uuid.UUID(user.ID.Bytes).String(),
		Name: deviceName,
		Type: deviceType,
	})

	return c.JSON(http.StatusOK, map[string]interface{}{
		"sync_status":      "success",
		"progress_updated": true,
		"percentage":       percentage,
		"current_page":     currentPage,
		"total_pages":      totalPages,
		"last_read_at":     updated.LastReadAt.Time,
	})
}

// GetProgressHistory retrieves reading session history
func (h *Handler) GetProgressHistory(c *echo.Context) error {
	user := MustGetAuthenticatedUser(c)

	mediaItemID, err := uuid.Parse(c.Param("id"))
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "invalid media item ID")
	}

	limit := 50
	if l := c.QueryParam("limit"); l != "" {
		if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 100 {
			limit = parsed
		}
	}

	history, err := h.db.GetReadingHistory(context.Background(), database.GetReadingHistoryParams{
		UserID:      pgtype.UUID{Bytes: user.ID.Bytes, Valid: true},
		MediaItemID: pgtype.UUID{Bytes: mediaItemID, Valid: true},
		Limit:       int32(limit),
	})
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "failed to get history")
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"media_item_id": mediaItemID,
		"sessions":      history,
		"count":         len(history),
	})
}

type ProgressWithMedia struct {
	MediaItemID        uuid.UUID `json:"media_item_id"`
	Title              string    `json:"title"`
	Author             string    `json:"author"`
	CoverImagePath     string    `json:"cover_image_path"`
	Percentage         float64   `json:"percentage"`
	CurrentPage        int32     `json:"current_page"`
	TotalPages         int32     `json:"total_pages"`
	LastReadAt         time.Time `json:"last_read_at"`
	Epubcfi            string    `json:"epubcfi"`
	LastSyncDevice     string    `json:"last_sync_device"`
	DeviceName         string    `json:"device_name,omitempty"`
	DeviceType         string    `json:"device_type,omitempty"`
	DeviceIcon         string    `json:"device_icon,omitempty"`
	ProgressPercentage float64   `json:"-"`
	EpubCFI            string    `json:"-"`
	LastUpdated        string    `json:"-"`
	FormatGroup        string    `json:"format_group"`
	EstimatedPages     int       `json:"estimated_pages"`
}

// GetAllProgress retrieves all progress for a user with sync source info
func (h *Handler) GetAllProgress(c *echo.Context) error {
	user := MustGetAuthenticatedUser(c)

	progressList := []ProgressWithMedia{}

	mediaItems, err := h.db.ListMediaItems(c.Request().Context(), database.ListMediaItemsParams{
		Limit: 1000, Offset: 0,
	})
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "failed to get media items")
	}

	for _, mediaItem := range mediaItems {
		progress, err := h.db.GetUniversalProgress(c.Request().Context(), database.GetUniversalProgressParams{
			MediaItemID: mediaItem.ID,
			UserID:      user.ID,
		})
		if err != nil {
			continue
		}

		coverPath := utils.ResolveMediaURL(mediaItem.LibraryID, mediaItem.CoverImagePath)

		author := ""
		if mediaItem.Author.Valid {
			author = mediaItem.Author.String
		}

		epubcfi := ""
		if progress.Epubcfi.Valid {
			epubcfi = progress.Epubcfi.String
		}

		deviceName := ""
		if progress.LastSyncDevice.Valid {
			deviceName = progress.LastSyncDevice.String
		}

		lastUpdated := ""
		if progress.LastReadAt.Valid {
			lastUpdated = progress.LastReadAt.Time.Format("01-02-2006 03:04 PM")
		}

		progressList = append(progressList, ProgressWithMedia{
			MediaItemID:        progress.MediaItemID.Bytes,
			Title:              mediaItem.Title,
			Author:             author,
			CoverImagePath:     coverPath,
			Percentage:         progress.Percentage.Float64,
			CurrentPage:        progress.CurrentPage.Int32,
			TotalPages:         progress.TotalPages.Int32,
			LastReadAt:         progress.LastReadAt.Time,
			Epubcfi:            epubcfi,
			LastSyncDevice:     deviceName,
			ProgressPercentage: progress.Percentage.Float64 * 100,
			EpubCFI:            epubcfi,
			LastUpdated:        lastUpdated,
			DeviceIcon:         getDeviceIcon(deviceName),
			FormatGroup:        mediaItem.FormatGroup,
			EstimatedPages:     wsync.EstimatedPages(mediaItem.TotalCharacters.Int64),
		})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"progress": progressList,
		"total":    len(progressList),
	})
}

func (h *Handler) GetAllProgressData(c *echo.Context) ([]ProgressWithMedia, error) {
	user := MustGetAuthenticatedUser(c)

	progressList := []ProgressWithMedia{}

	mediaItems, err := h.db.ListMediaItems(c.Request().Context(), database.ListMediaItemsParams{
		Limit:  1000,
		Offset: 0,
	})
	if err != nil {
		return nil, err
	}

	for _, mediaItem := range mediaItems {
		progress, err := h.db.GetUniversalProgress(c.Request().Context(), database.GetUniversalProgressParams{
			MediaItemID: mediaItem.ID,
			UserID:      user.ID,
		})
		if err != nil {
			continue
		}

		coverPath := utils.ResolveMediaURL(mediaItem.LibraryID, mediaItem.CoverImagePath)

		author := ""
		if mediaItem.Author.Valid {
			author = mediaItem.Author.String
		}

		epubcfi := ""
		if progress.Epubcfi.Valid {
			epubcfi = progress.Epubcfi.String
		}

		deviceName := ""
		if progress.LastSyncDevice.Valid {
			deviceName = progress.LastSyncDevice.String
		}

		lastUpdated := ""
		if progress.LastReadAt.Valid {
			lastUpdated = progress.LastReadAt.Time.Format("01-02-2006 03:04 PM")
		}

		progressList = append(progressList, ProgressWithMedia{
			MediaItemID:        progress.MediaItemID.Bytes,
			Title:              mediaItem.Title,
			Author:             author,
			CoverImagePath:     coverPath,
			Percentage:         progress.Percentage.Float64,
			CurrentPage:        progress.CurrentPage.Int32,
			TotalPages:         progress.TotalPages.Int32,
			LastReadAt:         progress.LastReadAt.Time,
			Epubcfi:            epubcfi,
			LastSyncDevice:     deviceName,
			ProgressPercentage: progress.Percentage.Float64 * 100,
			EpubCFI:            epubcfi,
			LastUpdated:        lastUpdated,
			DeviceIcon:         getDeviceIcon(deviceName),
			DeviceName:         deviceName,
			DeviceType:         deviceName,
			FormatGroup:        mediaItem.FormatGroup,
			EstimatedPages:     wsync.EstimatedPages(mediaItem.TotalCharacters.Int64),
		})
	}

	return progressList, nil
}
