package api

import (
	"context"
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/rycroftapparel/workpulse-api/internal/httpapi"
)

// recordUserActivity stores an audit row for the given user (best-effort).
func (s *Server) recordUserActivity(c *gin.Context, ctx context.Context, userID uint64, action, title, detail string, meta map[string]interface{}) {
	if userID == 0 || strings.TrimSpace(action) == "" {
		return
	}
	var metaRaw interface{}
	if len(meta) > 0 {
		b, err := json.Marshal(meta)
		if err == nil {
			metaRaw = string(b)
		}
	}
	ip := ""
	ua := ""
	if c != nil {
		ip = strings.TrimSpace(c.ClientIP())
		ua = strings.TrimSpace(c.GetHeader("User-Agent"))
		if len(ua) > 512 {
			ua = ua[:512]
		}
	}
	_, _ = s.DB.ExecContext(ctx,
		`INSERT INTO user_activity_logs (user_id, action, title, detail, meta, ip_address, user_agent)
		 VALUES ($1, $2, $3, NULLIF($4, ''), $5::jsonb, NULLIF($6, ''), NULLIF($7, ''))`,
		userID, strings.TrimSpace(action), strings.TrimSpace(title), strings.TrimSpace(detail), metaRaw, ip, ua,
	)
}

func (s *Server) recordAuthActivity(c *gin.Context, ctx context.Context, userID uint64, action, title, methodDetail, email, name string) {
	display := strings.TrimSpace(name)
	if display == "" {
		display = strings.TrimSpace(email)
	}
	detail := display
	if email != "" && !strings.EqualFold(display, email) {
		detail = display + " · " + email
	}
	if strings.TrimSpace(methodDetail) != "" {
		if detail != "" {
			detail += " — " + methodDetail
		} else {
			detail = methodDetail
		}
	}
	meta := map[string]interface{}{}
	if email != "" {
		meta["email"] = email
	}
	if name != "" {
		meta["name"] = name
	}
	s.recordUserActivity(c, ctx, userID, action, title, detail, meta)
}

func activityLogItemFromScan(id, userID uint64, action, title, detail, ip, ua string, metaRaw []byte, at time.Time, actorName, actorEmail string) gin.H {
	var meta interface{}
	if len(metaRaw) > 0 {
		_ = json.Unmarshal(metaRaw, &meta)
	}
	return gin.H{
		"id":         id,
		"userId":     userID,
		"action":     action,
		"title":      title,
		"detail":     strings.TrimSpace(detail),
		"meta":       meta,
		"ipAddress":  ip,
		"userAgent":  ua,
		"at":         at,
		"actorName":  strings.TrimSpace(actorName),
		"actorEmail": strings.TrimSpace(actorEmail),
	}
}

type activityLogQuery struct {
	userID     uint64 // 0 = all users (superadmin team view)
	authOnly   bool
	limit      int
	offset     int
}

func (s *Server) queryActivityLogs(ctx context.Context, q activityLogQuery) ([]gin.H, int, error) {
	where := `WHERE 1=1`
	args := []interface{}{}
	n := 1
	if q.userID > 0 {
		where += ` AND l.user_id = $` + strconv.Itoa(n)
		args = append(args, q.userID)
		n++
	}
	if q.authOnly {
		where += ` AND l.action LIKE 'auth.%'`
	}
	args = append(args, q.limit, q.offset)

	sqlQ := `SELECT l.id, l.user_id, l.action, l.title, COALESCE(l.detail, ''), l.meta,
		COALESCE(l.ip_address, ''), COALESCE(l.user_agent, ''), l.created_at,
		COALESCE(NULLIF(TRIM(u.name), ''), u.email), u.email
		FROM user_activity_logs l
		JOIN users u ON u.id = l.user_id ` + where + `
		ORDER BY l.created_at DESC LIMIT $` + strconv.Itoa(n) + ` OFFSET $` + strconv.Itoa(n+1)

	rows, err := s.DB.QueryContext(ctx, sqlQ, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	items := []gin.H{}
	for rows.Next() {
		var id, rowUID uint64
		var action, title, detail, ip, ua, actorName, actorEmail string
		var metaRaw []byte
		var at time.Time
		if err := rows.Scan(&id, &rowUID, &action, &title, &detail, &metaRaw, &ip, &ua, &at, &actorName, &actorEmail); err != nil {
			return nil, 0, err
		}
		items = append(items, activityLogItemFromScan(id, rowUID, action, title, detail, ip, ua, metaRaw, at, actorName, actorEmail))
	}
	if err := rows.Err(); err != nil {
		return nil, 0, err
	}

	countQ := `SELECT COUNT(*) FROM user_activity_logs l ` + where
	countArgs := args[:len(args)-2]
	var total int
	if err := s.DB.QueryRowContext(ctx, countQ, countArgs...).Scan(&total); err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

func (s *Server) deleteActivityLogs(ctx context.Context, userID uint64, authOnly bool) (int64, error) {
	if userID > 0 {
		if authOnly {
			res, err := s.DB.ExecContext(ctx, `DELETE FROM user_activity_logs WHERE user_id = $1 AND action LIKE 'auth.%'`, userID)
			if err != nil {
				return 0, err
			}
			return res.RowsAffected()
		}
		res, err := s.DB.ExecContext(ctx, `DELETE FROM user_activity_logs WHERE user_id = $1`, userID)
		if err != nil {
			return 0, err
		}
		return res.RowsAffected()
	}
	if authOnly {
		res, err := s.DB.ExecContext(ctx, `DELETE FROM user_activity_logs WHERE action LIKE 'auth.%'`)
		if err != nil {
			return 0, err
		}
		return res.RowsAffected()
	}
	res, err := s.DB.ExecContext(ctx, `DELETE FROM user_activity_logs`)
	if err != nil {
		return 0, err
	}
	return res.RowsAffected()
}

func activityLogListParams(c *gin.Context) (limit, offset int) {
	limit, _ = strconv.Atoi(c.DefaultQuery("limit", "30"))
	if limit < 1 {
		limit = 1
	}
	if limit > 100 {
		limit = 100
	}
	offset, _ = strconv.Atoi(c.DefaultQuery("offset", "0"))
	if offset < 0 {
		offset = 0
	}
	return limit, offset
}

// getMeActivityLog returns the authenticated user's audit timeline (GET /me/activity-log).
// Superadmin: view=team lists login/logout for all users (same session, no separate admin URL).
func (s *Server) getMeActivityLog(c *gin.Context) {
	uid := userID(c)
	limit, offset := activityLogListParams(c)
	view := strings.ToLower(strings.TrimSpace(c.Query("view")))

	ctx, cancel := s.ctx(c)
	defer cancel()

	var isActive bool
	if err := s.DB.QueryRowContext(ctx, `SELECT is_active FROM users WHERE id = $1`, uid).Scan(&isActive); err != nil || !isActive {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, httpapi.Fail("not_found", "user not found"))
			return
		}
		if err != nil {
			c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
			return
		}
		c.JSON(http.StatusForbidden, httpapi.Fail("forbidden", "account is disabled"))
		return
	}

	q := activityLogQuery{userID: uid, limit: limit, offset: offset}
	if view == "team" {
		if roleFromContext(c) != "superadmin" {
			c.JSON(http.StatusForbidden, httpapi.Fail("forbidden", "superadmin role required for team activity log"))
			return
		}
		q.userID = 0
		q.authOnly = true
	}

	items, total, err := s.queryActivityLogs(ctx, q)
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
		return
	}

	c.JSON(http.StatusOK, httpapi.OK(gin.H{
		"items":   items,
		"total":   total,
		"limit":   limit,
		"offset":  offset,
		"hasMore": offset+len(items) < total,
	}))
}

// deleteMeActivityLog clears logs for the current user (DELETE /me/activity-log). scope=auth keeps non-auth rows.
func (s *Server) deleteMeActivityLog(c *gin.Context) {
	uid := userID(c)
	scope := strings.ToLower(strings.TrimSpace(c.DefaultQuery("scope", "all")))
	view := strings.ToLower(strings.TrimSpace(c.Query("view")))

	ctx, cancel := s.ctx(c)
	defer cancel()

	var deleted int64
	var err error
	if view == "team" {
		if roleFromContext(c) != "superadmin" {
			c.JSON(http.StatusForbidden, httpapi.Fail("forbidden", "superadmin role required"))
			return
		}
		deleted, err = s.deleteActivityLogs(ctx, 0, scope == "auth" || scope == "")
	} else {
		deleted, err = s.deleteActivityLogs(ctx, uid, scope == "auth")
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
		return
	}

	c.JSON(http.StatusOK, httpapi.OK(gin.H{"deleted": deleted}))
}

// getAdminActivityLog lists workspace activity (superadmin). scope=auth limits to login/logout.
func (s *Server) getAdminActivityLog(c *gin.Context) {
	limit, offset := activityLogListParams(c)
	scope := strings.ToLower(strings.TrimSpace(c.DefaultQuery("scope", "auth")))
	userFilter, _ := strconv.ParseUint(c.Query("userId"), 10, 64)

	ctx, cancel := s.ctx(c)
	defer cancel()

	q := activityLogQuery{limit: limit, offset: offset}
	if scope == "auth" || scope == "" {
		q.authOnly = true
	}
	if userFilter > 0 {
		q.userID = userFilter
	}

	items, total, err := s.queryActivityLogs(ctx, q)
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
		return
	}

	c.JSON(http.StatusOK, httpapi.OK(gin.H{
		"items":   items,
		"total":   total,
		"limit":   limit,
		"offset":  offset,
		"hasMore": offset+len(items) < total,
	}))
}

// deleteAdminActivityLog clears workspace logs (superadmin). Default scope=auth.
func (s *Server) deleteAdminActivityLog(c *gin.Context) {
	scope := strings.ToLower(strings.TrimSpace(c.DefaultQuery("scope", "auth")))
	ctx, cancel := s.ctx(c)
	defer cancel()

	deleted, err := s.deleteActivityLogs(ctx, 0, scope == "auth" || scope == "")
	if err != nil {
		c.JSON(http.StatusInternalServerError, httpapi.Fail("db", err.Error()))
		return
	}
	c.JSON(http.StatusOK, httpapi.OK(gin.H{"deleted": deleted}))
}
