package api

import (
	"errors"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"
	"github.com/jackc/pgx/v5/pgtype"

	"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
	"git.fabledsword.com/bvandeusen/minstrel/internal/lidarr"
	"git.fabledsword.com/bvandeusen/minstrel/internal/lidarrquarantine"
)

// adminQueueRowView is a single aggregated row in the admin queue response.
type adminQueueRowView struct {
	TrackID         string                 `json:"track_id"`
	TrackTitle      string                 `json:"track_title"`
	ArtistName      string                 `json:"artist_name"`
	AlbumTitle      string                 `json:"album_title"`
	AlbumID         string                 `json:"album_id"`
	LidarrAlbumMBID *string                `json:"lidarr_album_mbid,omitempty"`
	ReportCount     int32                  `json:"report_count"`
	LatestAt        string                 `json:"latest_at"`
	ReasonCounts    map[string]int         `json:"reason_counts"`
	Reports         []adminQueueReportView `json:"reports"`
}

// adminQueueReportView is one user's report under an aggregated admin row.
type adminQueueReportView struct {
	UserID    string  `json:"user_id"`
	Username  string  `json:"username"`
	Reason    string  `json:"reason"`
	Notes     *string `json:"notes,omitempty"`
	CreatedAt string  `json:"created_at"`
}

// formatTimestamp renders a pgtype.Timestamptz as RFC3339 or empty string.
func formatTimestamp(ts pgtype.Timestamptz) string {
	if !ts.Valid {
		return ""
	}
	return ts.Time.Format("2006-01-02T15:04:05Z07:00")
}

// handleListAdminQuarantine implements GET /api/admin/quarantine.
func (h *handlers) handleListAdminQuarantine(w http.ResponseWriter, r *http.Request) {
	rows, err := h.lidarrQuarantine.ListAdminQueue(r.Context())
	if err != nil {
		h.logger.Error("admin: list quarantine", "err", err)
		writeAdminJSONErr(w, http.StatusInternalServerError, "server_error")
		return
	}
	out := make([]adminQueueRowView, 0, len(rows))
	for _, row := range rows {
		reports := make([]adminQueueReportView, 0, len(row.Reports))
		for _, rep := range row.Reports {
			reports = append(reports, adminQueueReportView{
				UserID:    uuidToString(rep.UserID),
				Username:  rep.Username,
				Reason:    rep.Reason,
				Notes:     rep.Notes,
				CreatedAt: formatTimestamp(rep.CreatedAt),
			})
		}
		out = append(out, adminQueueRowView{
			TrackID:         uuidToString(row.TrackID),
			TrackTitle:      row.TrackTitle,
			ArtistName:      row.ArtistName,
			AlbumTitle:      row.AlbumTitle,
			AlbumID:         uuidToString(row.AlbumID),
			LidarrAlbumMBID: row.LidarrAlbumMBID,
			ReportCount:     row.ReportCount,
			LatestAt:        formatTimestamp(row.LatestAt),
			ReasonCounts:    row.ReasonCounts,
			Reports:         reports,
		})
	}
	writeJSON(w, http.StatusOK, out)
}

// actionResultView is the response shape for the three admin destructive
// actions (resolve, delete-file, delete-via-lidarr).
type actionResultView struct {
	ActionID          string `json:"action_id"`
	AffectedUsers     int32  `json:"affected_users"`
	DeletedTrackCount *int   `json:"deleted_track_count,omitempty"`
}

// handleResolveQuarantine implements POST /api/admin/quarantine/{track_id}/resolve.
func (h *handlers) handleResolveQuarantine(w http.ResponseWriter, r *http.Request) {
	admin, ok := auth.UserFromContext(r.Context())
	if !ok {
		writeAdminJSONErr(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	id, ok := parseUUID(chi.URLParam(r, "track_id"))
	if !ok {
		writeAdminJSONErr(w, http.StatusBadRequest, "invalid_id")
		return
	}
	action, err := h.lidarrQuarantine.Resolve(r.Context(), id, admin.ID)
	if err != nil {
		if errors.Is(err, lidarrquarantine.ErrTrackNotFound) {
			writeAdminJSONErr(w, http.StatusNotFound, "track_not_found")
			return
		}
		h.logger.Error("admin: resolve quarantine", "err", err)
		writeAdminJSONErr(w, http.StatusInternalServerError, "server_error")
		return
	}
	// Broadcast: affects every user who'd flagged this track.
	h.publishQuarantineEvent("quarantine.resolved", pgtype.UUID{}, id, true)
	writeJSON(w, http.StatusOK, actionResultView{
		ActionID:      uuidToString(action.ID),
		AffectedUsers: action.AffectedUsers,
	})
}

// handleDeleteQuarantineFile implements POST /api/admin/quarantine/{track_id}/delete-file.
func (h *handlers) handleDeleteQuarantineFile(w http.ResponseWriter, r *http.Request) {
	admin, ok := auth.UserFromContext(r.Context())
	if !ok {
		writeAdminJSONErr(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	id, ok := parseUUID(chi.URLParam(r, "track_id"))
	if !ok {
		writeAdminJSONErr(w, http.StatusBadRequest, "invalid_id")
		return
	}
	action, err := h.lidarrQuarantine.DeleteFile(r.Context(), id, admin.ID)
	if err != nil {
		switch {
		case errors.Is(err, lidarrquarantine.ErrTrackNotFound):
			writeAdminJSONErr(w, http.StatusNotFound, "track_not_found")
		default:
			h.logger.Error("admin: delete file", "err", err)
			writeAdminJSONErr(w, http.StatusInternalServerError, "file_delete_failed")
		}
		return
	}
	// Broadcast: the track row is gone; every client's library/likes/queue
	// invalidates so stale references stop showing.
	h.publishQuarantineEvent("quarantine.file_deleted", pgtype.UUID{}, id, true)
	writeJSON(w, http.StatusOK, actionResultView{
		ActionID:      uuidToString(action.ID),
		AffectedUsers: action.AffectedUsers,
	})
}

// handleDeleteQuarantineViaLidarr implements POST /api/admin/quarantine/{track_id}/delete-via-lidarr.
func (h *handlers) handleDeleteQuarantineViaLidarr(w http.ResponseWriter, r *http.Request) {
	admin, ok := auth.UserFromContext(r.Context())
	if !ok {
		writeAdminJSONErr(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	id, ok := parseUUID(chi.URLParam(r, "track_id"))
	if !ok {
		writeAdminJSONErr(w, http.StatusBadRequest, "invalid_id")
		return
	}
	action, deleted, err := h.lidarrQuarantine.DeleteViaLidarr(r.Context(), id, admin.ID)
	if err != nil {
		switch {
		case errors.Is(err, lidarrquarantine.ErrLidarrDisabled):
			writeAdminJSONErr(w, http.StatusServiceUnavailable, "lidarr_disabled")
		case errors.Is(err, lidarrquarantine.ErrTrackNotFound):
			writeAdminJSONErr(w, http.StatusNotFound, "track_not_found")
		case errors.Is(err, lidarrquarantine.ErrAlbumMBIDMissing):
			writeAdminJSONErr(w, http.StatusNotFound, "album_mbid_missing")
		case errors.Is(err, lidarrquarantine.ErrLidarrAlbumNotFound):
			writeAdminJSONErr(w, http.StatusBadGateway, "lidarr_album_lookup_failed")
		case errors.Is(err, lidarr.ErrUnreachable):
			writeAdminJSONErr(w, http.StatusServiceUnavailable, "lidarr_unreachable")
		case errors.Is(err, lidarr.ErrAuthFailed):
			writeAdminJSONErr(w, http.StatusServiceUnavailable, "lidarr_auth_failed")
		default:
			h.logger.Error("admin: delete via lidarr", "err", err)
			writeAdminJSONErr(w, http.StatusInternalServerError, "server_error")
		}
		return
	}
	// Broadcast: same rationale as delete-file — track row gone everywhere.
	h.publishQuarantineEvent("quarantine.deleted_via_lidarr", pgtype.UUID{}, id, true)
	writeJSON(w, http.StatusOK, actionResultView{
		ActionID:          uuidToString(action.ID),
		AffectedUsers:     action.AffectedUsers,
		DeletedTrackCount: &deleted,
	})
}

// actionLogView is the row shape returned by GET /api/admin/quarantine/actions.
type actionLogView struct {
	ID              string  `json:"id"`
	TrackID         string  `json:"track_id"`
	TrackTitle      string  `json:"track_title"`
	ArtistName      string  `json:"artist_name"`
	AlbumTitle      *string `json:"album_title,omitempty"`
	Action          string  `json:"action"`
	AdminID         *string `json:"admin_id,omitempty"`
	LidarrAlbumMBID *string `json:"lidarr_album_mbid,omitempty"`
	AffectedUsers   int32   `json:"affected_users"`
	CreatedAt       string  `json:"created_at"`
}

// handleListQuarantineActions implements GET /api/admin/quarantine/actions.
// ?limit= defaults to 50, capped at 200.
func (h *handlers) handleListQuarantineActions(w http.ResponseWriter, r *http.Request) {
	limitStr := r.URL.Query().Get("limit")
	limit := int32(50)
	if limitStr != "" {
		if v, err := strconv.Atoi(limitStr); err == nil && v > 0 {
			if v > 200 {
				v = 200
			}
			limit = int32(v)
		}
	}
	rows, err := dbq.New(h.pool).ListQuarantineActions(r.Context(), limit)
	if err != nil {
		h.logger.Error("admin: list actions", "err", err)
		writeAdminJSONErr(w, http.StatusInternalServerError, "server_error")
		return
	}
	out := make([]actionLogView, 0, len(rows))
	for _, row := range rows {
		var adminID *string
		if row.AdminID.Valid {
			s := uuidToString(row.AdminID)
			adminID = &s
		}
		out = append(out, actionLogView{
			ID:              uuidToString(row.ID),
			TrackID:         uuidToString(row.TrackID),
			TrackTitle:      row.TrackTitle,
			ArtistName:      row.ArtistName,
			AlbumTitle:      row.AlbumTitle,
			Action:          string(row.Action),
			AdminID:         adminID,
			LidarrAlbumMBID: row.LidarrAlbumMbid,
			AffectedUsers:   row.AffectedUsers,
			CreatedAt:       formatTimestamp(row.CreatedAt),
		})
	}
	writeJSON(w, http.StatusOK, out)
}
