package repository

import (
	"context"
	"database/sql"
	"fmt"
	"strings"
	"time"
)

type ActivityLogRow struct {
	CreatedAt         time.Time  `json:"created_at"`
	RequestStartedAt  *time.Time `json:"request_started_at,omitempty"`
	RequestFinishedAt *time.Time `json:"request_finished_at,omitempty"`
	DurationMs        *int       `json:"duration_ms,omitempty"`
	HttpStatus        *int       `json:"http_status,omitempty"`

	Username string `json:"username"`
	RoleCode string `json:"role_code"`

	ActionCategory string `json:"action_category"`
	ActionType     string `json:"action_type"`
	ActionTarget   string `json:"action_target"`
	Description    string `json:"description"`

	// ✅ TARGET USER
	TargetDfUsrID  int64  `json:"target_dfusr_id"`
	TargetUsername string `json:"target_username"`

	// ✅ DIFF
	ChangeBefore string `json:"change_before"`
	ChangeAfter  string `json:"change_after"`

	IpAddress string `json:"ip_address"`
	IsSuccess bool   `json:"is_success"`
	ErrorMsg  string `json:"error_message"`
	UserAgent string `json:"user_agent"`
	SessionID string `json:"session_id"`

	// audit ids
	UserUUID string `json:"user_id"`
	DfUsrID  int64  `json:"dfusr_id"`

	Email    string `json:"email"`
	IsActive bool   `json:"is_active"`
}

type ActivityLogQuery struct {
	Username       string
	RoleCode       string
	ActionCategory string
	ActionType     string
	ActionTarget   string
	Success        *bool

	StatusMin *int
	StatusMax *int

	DateFrom *time.Time
	DateTo   *time.Time

	Page  int
	Limit int
}

type ActivityLogResult struct {
	Total int64            `json:"total"`
	Items []ActivityLogRow `json:"items"`
}

func ListActivityLogs(
	ctx context.Context,
	db *sql.DB,
	q ActivityLogQuery,
) (ActivityLogResult, error) {

	// ---------- defaults ----------
	if q.Page <= 0 {
		q.Page = 1
	}
	// limit <=0 → unlimited
	if q.Limit <= 0 {
		q.Limit = 1000
	}
	if q.Limit > 1000 {
		q.Limit = 1000
	}

	offset := 0
	if q.Limit > 0 {
		offset = (q.Page - 1) * q.Limit
	}

	where := []string{}
	args := []interface{}{}

	add := func(cond string, val interface{}) {
		where = append(where, cond)
		args = append(args, val)
	}

	// ---------- filters ----------
	if strings.TrimSpace(q.Username) != "" {
		add(fmt.Sprintf("l.username ILIKE $%d", len(args)+1),
			"%"+strings.TrimSpace(q.Username)+"%")
	}
	if strings.TrimSpace(q.RoleCode) != "" {
		add(fmt.Sprintf("r.code = $%d", len(args)+1),
			strings.TrimSpace(q.RoleCode))
	}
	if strings.TrimSpace(q.ActionCategory) != "" {
		add(fmt.Sprintf("l.action_category = $%d", len(args)+1),
			strings.TrimSpace(q.ActionCategory))
	}
	if strings.TrimSpace(q.ActionType) != "" {
		add(fmt.Sprintf("l.action_type = $%d", len(args)+1),
			strings.TrimSpace(q.ActionType))
	}
	if strings.TrimSpace(q.ActionTarget) != "" {
		add(fmt.Sprintf("l.action_target ILIKE $%d", len(args)+1),
			"%"+strings.TrimSpace(q.ActionTarget)+"%")
	}
	if q.Success != nil {
		add(fmt.Sprintf("l.is_success = $%d", len(args)+1), *q.Success)
	}
	if q.StatusMin != nil {
		add(fmt.Sprintf("l.http_status >= $%d", len(args)+1), *q.StatusMin)
	}
	if q.StatusMax != nil {
		add(fmt.Sprintf("l.http_status <= $%d", len(args)+1), *q.StatusMax)
	}
	if q.DateFrom != nil {
		add(fmt.Sprintf("l.created_at >= $%d", len(args)+1), *q.DateFrom)
	}
	if q.DateTo != nil {
		add(fmt.Sprintf("l.created_at < $%d", len(args)+1),
			q.DateTo.Add(24*time.Hour))
	}

	whereSQL := ""
	if len(where) > 0 {
		whereSQL = "WHERE " + strings.Join(where, " AND ")
	}

	// ---------- COUNT ----------
	countSQL := fmt.Sprintf(`
		SELECT count(*)
		FROM mk_user_activity_log l
		LEFT JOIN mk_dfusr u ON u.id = l.dfusr_id
		LEFT JOIN dfrole_usr ru ON ru.dfusr_id = u.id
		LEFT JOIN dfrole r ON r.id = ru.dfrole_id
		%s
	`, whereSQL)

	var total int64
	if err := db.QueryRowContext(ctx, countSQL, args...).Scan(&total); err != nil {
		return ActivityLogResult{}, err
	}

	// ---------- LIST ----------
	listArgs := append([]interface{}{}, args...)

	listSQL := fmt.Sprintf(`
SELECT
  l.created_at,
  l.request_started_at,
  l.request_finished_at,
  l.duration_ms,
  l.http_status,

  COALESCE(u.username, l.username, '') AS username,
  COALESCE(r.code,''),

  COALESCE(l.action_category,''),
  COALESCE(l.action_type,''),
  COALESCE(l.action_target,''),
  COALESCE(l.description,''),

  COALESCE(l.target_dfusr_id, 0),
  COALESCE(l.target_username, ''),
  COALESCE(l.change_before::text, ''),
  COALESCE(l.change_after::text, ''),

  COALESCE(l.ip_address,''),
  COALESCE(l.is_success,false),
  COALESCE(l.error_message,''),
  COALESCE(l.user_agent,''),
  COALESCE(l.session_id,''),

  COALESCE(l.user_id::text,''),
  COALESCE(l.dfusr_id,0),

  COALESCE(u.email,''),
  COALESCE(u.is_active,false)

FROM mk_user_activity_log l
LEFT JOIN mk_dfusr u ON u.id = l.dfusr_id
LEFT JOIN dfrole_usr ru ON ru.dfusr_id = u.id
LEFT JOIN dfrole r ON r.id = ru.dfrole_id
%s
ORDER BY l.created_at DESC
`, whereSQL)
	listSQL += fmt.Sprintf(
		" LIMIT $%d OFFSET $%d",
		len(listArgs)+1,
		len(listArgs)+2,
	)

	listArgs = append(listArgs, q.Limit, offset)

	rows, err := db.QueryContext(ctx, listSQL, listArgs...)
	if err != nil {
		return ActivityLogResult{}, err
	}
	defer rows.Close()

	items := []ActivityLogRow{}

	for rows.Next() {
		var it ActivityLogRow
		var started, finished sql.NullTime
		var dur, status sql.NullInt64

		if err := rows.Scan(
			&it.CreatedAt,
			&started,
			&finished,
			&dur,
			&status,

			&it.Username,
			&it.RoleCode,

			&it.ActionCategory,
			&it.ActionType,
			&it.ActionTarget,
			&it.Description,

			// ✅ NEW
			&it.TargetDfUsrID,
			&it.TargetUsername,
			&it.ChangeBefore,
			&it.ChangeAfter,

			&it.IpAddress,
			&it.IsSuccess,
			&it.ErrorMsg,
			&it.UserAgent,
			&it.SessionID,

			&it.UserUUID,
			&it.DfUsrID,

			&it.Email,
			&it.IsActive,
		); err != nil {
			return ActivityLogResult{}, err
		}

		if started.Valid {
			it.RequestStartedAt = &started.Time
		}
		if finished.Valid {
			it.RequestFinishedAt = &finished.Time
		}
		if dur.Valid {
			v := int(dur.Int64)
			it.DurationMs = &v
		}
		if status.Valid {
			v := int(status.Int64)
			it.HttpStatus = &v
		}

		items = append(items, it)
	}

	return ActivityLogResult{
		Total: total,
		Items: items,
	}, nil
}
