package router

import (
	"context"
	"log"
	"net/url"
	"time"

	"bookhoard/internal/database"
	"bookhoard/templates"

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

func getTemplateUserWithTheme(c *echo.Context, cfg *Config) (templates.User, error) {
	userID := c.Get("user_id").(string)
	userEmail := c.Get("user_email").(string)
	userUsername := c.Get("user_username").(string)
	userRole := c.Get("user_role").(string)

	userUUID, err := uuid.Parse(userID)
	if err != nil {
		log.Printf("getTemplateUserWithTheme failed: invalid UUID '%s': %v", userID, err)
		return templates.User{}, err
	}

	userDB, err := cfg.Queries.GetUser(c.Request().Context(), uuidToPGType(userUUID))
	if err != nil {
		log.Printf("getTemplateUserWithTheme failed: database query error for user ID %s: %v", userID, err)
		return templates.User{}, err
	}

	userTheme := "tokyo-night"
	if userDB.Theme.Valid {
		userTheme = userDB.Theme.String
	}

	userTimezone := "UTC"
	if userDB.Timezone.Valid {
		userTimezone = userDB.Timezone.String
	}

	// Extract JWT token for WebSocket authentication
	token := ""
	if cookie, err := c.Cookie("token"); err == nil {
		token = cookie.Value
	}

	return templates.User{
		ID:       userID,
		Email:    userEmail,
		Username: userUsername,
		Role:     userRole,
		Theme:    userTheme,
		Token:    token,
		Timezone: userTimezone,
	}, nil
}

func convertPending(pending []map[string]interface{}) []templates.PendingRegistrationData {
	result := make([]templates.PendingRegistrationData, len(pending))
	for i, p := range pending {
		result[i] = templates.PendingRegistrationData{
			RegistrationID: p["registration_id"].(string),
			DeviceName:     p["device_name"].(string),
			DeviceType:     p["device_type"].(string),
			ExpiresAt:      p["expires_at"].(time.Time).Format(time.RFC3339),
		}
	}
	return result
}

func pingDB(cfg *Config, ctx context.Context) error {
	if cfg.DBPool != nil {
		if pool, ok := cfg.DBPool.(*pgxpool.Pool); ok {
			return pool.Ping(ctx)
		}
	}
	return nil
}

func parseUUID(s string) (uuid.UUID, error) {
	return uuid.Parse(s)
}

func uuidToPGType(u uuid.UUID) pgtype.UUID {
	return pgtype.UUID{Bytes: u, Valid: true}
}

const selectedLibraryCookie = "selectedLibrary"
const allLibrariesSentinel = "__all__"

type LibraryResolution struct {
	LibraryID  string
	IsAll      bool
	LibUUID    pgtype.UUID
	Libraries  []templates.LibraryData
	FirstID    string
}

func getText(t pgtype.Text) string {
	if t.Valid {
		return t.String
	}
	return ""
}

func resolveLibrary(c *echo.Context, cfg *Config, userUUID string) LibraryResolution {
	res := LibraryResolution{}

	userU, _ := uuid.Parse(userUUID)
	libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userU))
	if err != nil {
		log.Printf("GetUserVisibleLibraries failed: %v", err)
		libraries = []database.GetUserVisibleLibrariesRow{}
	}

	counts, countErr := cfg.Queries.GetVisibleLibraryMediaCounts(c.Request().Context(), uuidToPGType(userU))
	if countErr != nil {
		log.Printf("GetVisibleLibraryMediaCounts failed: %v", countErr)
		counts = []database.GetVisibleLibraryMediaCountsRow{}
	}
	countMap := make(map[string]int64, len(counts))
	for _, mc := range counts {
		mcUUID, _ := uuid.FromBytes(mc.ID.Bytes[0:16])
		countMap[mcUUID.String()] = mc.MediaCount
	}

	res.Libraries = make([]templates.LibraryData, len(libraries))
	for i, lib := range libraries {
		libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
		res.Libraries[i] = templates.LibraryData{
			ID:          libUUID.String(),
			Name:        lib.Name,
			Description: getText(lib.Description),
			TypeName:    lib.TypeName,
			MediaCount:  countMap[libUUID.String()],
		}
	}

	if len(libraries) > 0 {
		libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
		res.FirstID = libUUID.String()
	}

	libraryID := c.QueryParam("library_id")
	if libraryID == "" {
		if cookie, err := c.Cookie(selectedLibraryCookie); err == nil {
			val, _ := url.QueryUnescape(cookie.Value)
			if val == allLibrariesSentinel {
				res.IsAll = true
				res.LibraryID = ""
				return res
			}
			if _, parseErr := uuid.Parse(val); parseErr == nil {
				libraryID = val
			}
		}
	}

	if libraryID == "" {
		res.LibraryID = res.FirstID
		if res.LibraryID != "" {
			parsed, _ := uuid.Parse(res.LibraryID)
			res.LibUUID = pgtype.UUID{Bytes: parsed, Valid: true}
		}
		return res
	}

	res.LibraryID = libraryID
	parsed, _ := uuid.Parse(libraryID)
	res.LibUUID = pgtype.UUID{Bytes: parsed, Valid: true}
	return res
}
