package handlers

import (
	"fmt"
	"os"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5/pgxpool"
	"ticketing-backend/internal/config"
	"ticketing-backend/internal/utils"
)

type SuperAdminHandler struct {
	pool     *pgxpool.Pool
	settings *config.AppSettings
}

func NewSuperAdminHandler(pool *pgxpool.Pool, settings *config.AppSettings) *SuperAdminHandler {
	return &SuperAdminHandler{pool: pool, settings: settings}
}

func (h *SuperAdminHandler) DashboardStats(c *gin.Context) {
	var totalUsers, totalEvents, totalOrders, totalAttendees int
	var totalRevenueFloat float64
	h.pool.QueryRow(c.Request.Context(), `SELECT COUNT(*) FROM users`).Scan(&totalUsers)
	h.pool.QueryRow(c.Request.Context(), `SELECT COUNT(*) FROM events`).Scan(&totalEvents)
	h.pool.QueryRow(c.Request.Context(), `SELECT COUNT(*) FROM orders WHERE status = 'completed'`).Scan(&totalOrders)
	h.pool.QueryRow(c.Request.Context(), `SELECT COALESCE(SUM(total_amount), 0) FROM orders WHERE status = 'completed'`).Scan(&totalRevenueFloat)
	h.pool.QueryRow(c.Request.Context(), `SELECT COUNT(*) FROM tickets`).Scan(&totalAttendees)

	rows, _ := h.pool.Query(c.Request.Context(),
		`SELECT o.id, o.total_amount, o.status, o.created_at, e.name as event_name,
		        u.full_name as buyer_name
		 FROM orders o
		 JOIN events e ON o.event_id = e.id
		 JOIN users u ON u.id = o.buyer_id
		 ORDER BY o.created_at DESC LIMIT 10`)
	defer rows.Close()

	type orderRow struct {
		ID, Status, CreatedAt, EventName, BuyerName string
		TotalAmount float64
	}
	var recentOrders []gin.H
	for rows.Next() {
		var r orderRow
		rows.Scan(&r.ID, &r.TotalAmount, &r.Status, &r.CreatedAt, &r.EventName, &r.BuyerName)
		recentOrders = append(recentOrders, gin.H{
			"id": r.ID, "total_amount": r.TotalAmount, "status": r.Status,
			"created_at": r.CreatedAt, "event_name": r.EventName, "buyer_name": r.BuyerName,
		})
	}

	utils.Success(c, gin.H{
		"totalUsers":     totalUsers,
		"totalEvents":    totalEvents,
		"totalOrders":    totalOrders,
		"totalRevenue":   totalRevenueFloat,
		"totalAttendees": totalAttendees,
		"recentOrders":   recentOrders,
	})
}

func (h *SuperAdminHandler) ListAllOrders(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
	offset := (page - 1) * limit
	status := c.Query("status")

	q := `SELECT o.id, o.total_amount, o.status, o.created_at, e.name as event_name,
		u.full_name as buyer_name
		FROM orders o
		JOIN events e ON o.event_id = e.id
		JOIN users u ON u.id = o.buyer_id`
	var args []interface{}
	argIdx := 1

	if status != "" {
		q += fmt.Sprintf(` WHERE o.status = $%d`, argIdx)
		args = append(args, status)
		argIdx++
	}

	q += ` ORDER BY o.created_at DESC`
	q += fmt.Sprintf(` LIMIT $%d OFFSET $%d`, argIdx, argIdx+1)
	args = append(args, limit, offset)

	rows, _ := h.pool.Query(c.Request.Context(), q, args...)
	defer rows.Close()

	type orderRow struct {
		ID, Status, CreatedAt, EventName, BuyerName string
		TotalAmount float64
	}
	var orders []gin.H
	for rows.Next() {
		var r orderRow
		rows.Scan(&r.ID, &r.TotalAmount, &r.Status, &r.CreatedAt, &r.EventName, &r.BuyerName)
		orders = append(orders, gin.H{
			"id": r.ID, "total_amount": r.TotalAmount, "status": r.Status,
			"created_at": r.CreatedAt, "event_name": r.EventName, "buyer_name": r.BuyerName,
		})
	}

	var total int
	countQ := `SELECT COUNT(*) FROM orders o JOIN events e ON o.event_id = e.id JOIN users u ON u.id = o.buyer_id`
	if status != "" {
		countQ += ` WHERE o.status = $1`
		h.pool.QueryRow(c.Request.Context(), countQ, status).Scan(&total)
	} else {
		h.pool.QueryRow(c.Request.Context(), countQ).Scan(&total)
	}

	utils.Success(c, gin.H{
		"orders": orders,
		"total":  total,
		"page":   page,
		"limit":  limit,
	})
}

func (h *SuperAdminHandler) ListAllEvents(c *gin.Context) {
	rows, _ := h.pool.Query(c.Request.Context(),
		`SELECT e.id, e.name, e.venue, e.event_date, e.total_capacity, e.remaining_capacity,
		        e.status, e.war_mode, e.created_at, u.full_name as organizer_name
		 FROM events e
		 LEFT JOIN users u ON u.id = e.organizer_id
		 ORDER BY e.created_at DESC LIMIT 100`)
	defer rows.Close()

	type eventRow struct {
		ID, Name, Venue, Status, CreatedAt, OrgName string
		EventDate string
		Total, Remaining int
		WarMode bool
	}
	var events []gin.H
	for rows.Next() {
		var r eventRow
		rows.Scan(&r.ID, &r.Name, &r.Venue, &r.EventDate, &r.Total, &r.Remaining, &r.Status, &r.WarMode, &r.CreatedAt, &r.OrgName)
		events = append(events, gin.H{
			"id": r.ID, "name": r.Name, "venue": r.Venue, "event_date": r.EventDate,
			"total_capacity": r.Total, "remaining_capacity": r.Remaining,
			"status": r.Status, "war_mode": r.WarMode, "created_at": r.CreatedAt,
			"organizer_name": r.OrgName,
		})
	}

	utils.Success(c, gin.H{"events": events})
}

func (h *SuperAdminHandler) ListAllPayments(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
	offset := (page - 1) * limit

	rows, _ := h.pool.Query(c.Request.Context(),
		`SELECT p.id, p.order_id, p.amount, p.gateway, p.gateway_transaction_id,
		        p.status, p.created_at
		 FROM payments p
		 ORDER BY p.created_at DESC LIMIT $1 OFFSET $2`, limit, offset)
	defer rows.Close()

	type payRow struct {
		ID, OrderID, Gateway, Status, CreatedAt string
		Amount float64
		GtxID *string
	}
	var payments []gin.H
	for rows.Next() {
		var r payRow
		rows.Scan(&r.ID, &r.OrderID, &r.Amount, &r.Gateway, &r.GtxID, &r.Status, &r.CreatedAt)
		payments = append(payments, gin.H{
			"id": r.ID, "order_id": r.OrderID, "amount": r.Amount, "gateway": r.Gateway,
			"gateway_transaction_id": r.GtxID, "status": r.Status, "created_at": r.CreatedAt,
		})
	}

	utils.Success(c, gin.H{"payments": payments})
}

func (h *SuperAdminHandler) ListAllTickets(c *gin.Context) {
	rows, _ := h.pool.Query(c.Request.Context(),
		`SELECT t.id, t.booking_code, t.ticket_class_name, t.buyer_name, t.buyer_email,
		        t.event_name, t.event_date, t.status, t.used_at, t.created_at
		 FROM tickets t ORDER BY t.created_at DESC LIMIT 200`)
	defer rows.Close()

	type ticketRow struct {
		ID, BookingCode, TcName, BuyerName, BuyerEmail, EventName, Status, CreatedAt string
		EventDate string
		UsedAt *string
	}
	var tickets []gin.H
	for rows.Next() {
		var r ticketRow
		rows.Scan(&r.ID, &r.BookingCode, &r.TcName, &r.BuyerName, &r.BuyerEmail, &r.EventName, &r.EventDate, &r.Status, &r.UsedAt, &r.CreatedAt)
		tickets = append(tickets, gin.H{
			"id": r.ID, "booking_code": r.BookingCode, "ticket_class_name": r.TcName,
			"buyer_name": r.BuyerName, "buyer_email": r.BuyerEmail, "event_name": r.EventName,
			"event_date": r.EventDate, "status": r.Status, "used_at": r.UsedAt, "created_at": r.CreatedAt,
		})
	}

	utils.Success(c, gin.H{"tickets": tickets})
}

func (h *SuperAdminHandler) ManageClasses(c *gin.Context) {
	if c.Request.Method == "GET" {
		rows, _ := h.pool.Query(c.Request.Context(),
			`SELECT id, name, description, created_at FROM classes ORDER BY name ASC`)
		defer rows.Close()

		type classRow struct {
			ID, Name, CreatedAt string
			Desc *string
		}
		var classes []gin.H
		for rows.Next() {
			var r classRow
			rows.Scan(&r.ID, &r.Name, &r.Desc, &r.CreatedAt)
			classes = append(classes, gin.H{"id": r.ID, "name": r.Name, "description": r.Desc, "created_at": r.CreatedAt})
		}

		utils.Success(c, gin.H{"classes": classes})
		return
	}

	if c.Request.Method == "POST" {
		userID, _ := c.Get("userId")
		var req struct {
			Name        string  `json:"name" binding:"required"`
			Description *string `json:"description"`
		}
		if err := c.ShouldBindJSON(&req); err != nil {
			utils.BadRequest(c, "Invalid data")
			return
		}

		var id string
		err := h.pool.QueryRow(c.Request.Context(),
			`INSERT INTO classes (name, description, created_by) VALUES ($1, $2, $3) RETURNING id`,
			req.Name, req.Description, userID,
		).Scan(&id)
		if err != nil {
			utils.InternalError(c, "Failed to create class")
			return
		}

		utils.Created(c, gin.H{"id": id})
	}
}

func (h *SuperAdminHandler) UpdateClass(c *gin.Context) {
	classID := c.Param("classId")
	var data map[string]interface{}
	if err := c.ShouldBindJSON(&data); err != nil {
		utils.BadRequest(c, "Invalid data")
		return
	}

	sets := ""
	args := []interface{}{}
	idx := 1

	for _, field := range []string{"name", "description"} {
		if val, ok := data[field]; ok {
			if idx > 1 {
				sets += ", "
			}
			sets += fmt.Sprintf("%s = $%d", field, idx)
			args = append(args, val)
			idx++
		}
	}

	if idx == 1 {
		utils.BadRequest(c, "No fields to update")
		return
	}

	sets += ", updated_at = NOW()"
	args = append(args, classID)

	_, err := h.pool.Exec(c.Request.Context(),
		fmt.Sprintf(`UPDATE classes SET %s WHERE id = $%d`, sets, idx), args...)
	if err != nil {
		utils.InternalError(c, "Failed to update class")
		return
	}

	utils.Success(c, gin.H{"message": "Class updated"})
}

func (h *SuperAdminHandler) ListAllLogs(c *gin.Context) {
	rows, _ := h.pool.Query(c.Request.Context(),
		`SELECT sl.id, sl.log_type, sl.action, sl.resource_type, sl.resource_id,
		        sl.status, sl.ip_address, sl.created_at, u.email as user_email
		 FROM system_logs sl
		 LEFT JOIN users u ON u.id = sl.user_id
		 ORDER BY sl.created_at DESC LIMIT 200`)
	defer rows.Close()

	type logRow struct {
		ID, LogType, Action, CreatedAt string
		ResType, ResID, Status, IPAddr, UserEmail *string
	}
	var logs []gin.H
	for rows.Next() {
		var r logRow
		rows.Scan(&r.ID, &r.LogType, &r.Action, &r.ResType, &r.ResID, &r.Status, &r.IPAddr, &r.CreatedAt, &r.UserEmail)
		logs = append(logs, gin.H{
			"id": r.ID, "log_type": r.LogType, "action": r.Action,
			"resource_type": r.ResType, "resource_id": r.ResID, "status": r.Status,
			"ip_address": r.IPAddr, "created_at": r.CreatedAt, "user_email": r.UserEmail,
		})
	}

	utils.Success(c, gin.H{"logs": logs})
}

func (h *SuperAdminHandler) ImportUsers(c *gin.Context) {
	userID, _ := c.Get("userId")

	file, header, err := c.Request.FormFile("file")
	if err != nil {
		utils.BadRequest(c, "File is required")
		return
	}
	defer file.Close()

	filename := header.Filename

	var importID string
	h.pool.QueryRow(c.Request.Context(),
		`INSERT INTO user_imports (imported_by, filename, total_rows, status)
		 VALUES ($1, $2, 0, 'processing') RETURNING id`,
		userID, filename,
	).Scan(&importID)

	utils.Success(c, gin.H{"importId": importID, "message": "Import started"})
}

func (h *SuperAdminHandler) GetEnvStatus(c *gin.Context) {
	utils.Success(c, gin.H{
		"midtransConfigured": osGetEnv("MIDTRANS_SERVER_KEY") != "" || h.settings.GetPaymentMethods() != nil,
		"smtpConfigured":     osGetEnv("SMTP_HOST") != "",
		"jwtConfigured":      osGetEnv("JWT_SECRET") != "",
	})
}

func (h *SuperAdminHandler) ListAllUsers(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
	offset := (page - 1) * limit
	role := c.Query("role")

	q := `SELECT id, email, full_name, role, is_verified, created_at FROM users`
	var args []interface{}
	argIdx := 1

	if role != "" {
		q += fmt.Sprintf(` WHERE role = $%d`, argIdx)
		args = append(args, role)
		argIdx++
	}

	q += ` ORDER BY created_at DESC`
	q += fmt.Sprintf(` LIMIT $%d OFFSET $%d`, argIdx, argIdx+1)
	args = append(args, limit, offset)

	rows, _ := h.pool.Query(c.Request.Context(), q, args...)
	defer rows.Close()

	type userRow struct {
		ID, Email, FullName, Role, CreatedAt string
		IsVerified bool
	}
	var users []gin.H
	for rows.Next() {
		var r userRow
		rows.Scan(&r.ID, &r.Email, &r.FullName, &r.Role, &r.IsVerified, &r.CreatedAt)
		users = append(users, gin.H{
			"id": r.ID, "email": r.Email, "full_name": r.FullName, "role": r.Role,
			"is_verified": r.IsVerified, "created_at": r.CreatedAt,
		})
	}

	var total int
	countQ := `SELECT COUNT(*) FROM users`
	if role != "" {
		countQ += ` WHERE role = $1`
		h.pool.QueryRow(c.Request.Context(), countQ, role).Scan(&total)
	} else {
		h.pool.QueryRow(c.Request.Context(), countQ).Scan(&total)
	}

	utils.Success(c, gin.H{"users": users, "total": total, "page": page, "limit": limit})
}

func (h *SuperAdminHandler) DeleteUser(c *gin.Context) {
	userID := c.Param("userId")
	if userID == c.GetString("userId") {
		utils.BadRequest(c, "Cannot delete yourself")
		return
	}
	_, err := h.pool.Exec(c.Request.Context(), `DELETE FROM users WHERE id = $1`, userID)
	if err != nil {
		utils.InternalError(c, "Failed to delete user")
		return
	}
	utils.Success(c, gin.H{"message": "User deleted"})
}

func (h *SuperAdminHandler) ListAllAttendees(c *gin.Context) {
	search := c.Query("search")

	q := `SELECT t.id, t.booking_code, t.ticket_class_name, t.buyer_name, t.buyer_email,
		t.event_name, t.event_date, t.status, t.used_at, t.created_at FROM tickets t`
	var args []interface{}
	argIdx := 1

	if search != "" {
		q += fmt.Sprintf(` WHERE (t.buyer_name ILIKE $%d OR t.buyer_email ILIKE $%d OR t.booking_code ILIKE $%d)`, argIdx, argIdx, argIdx)
		args = append(args, "%"+search+"%")
		argIdx++
	}

	q += ` ORDER BY t.created_at DESC LIMIT 200`

	rows, _ := h.pool.Query(c.Request.Context(), q, args...)
	defer rows.Close()

	type attRow struct {
		ID, BookingCode, TcName, BuyerName, BuyerEmail, EventName, Status, CreatedAt string
		EventDate string
		UsedAt *string
	}
	var attendees []gin.H
	for rows.Next() {
		var r attRow
		rows.Scan(&r.ID, &r.BookingCode, &r.TcName, &r.BuyerName, &r.BuyerEmail, &r.EventName, &r.EventDate, &r.Status, &r.UsedAt, &r.CreatedAt)
		attendees = append(attendees, gin.H{
			"id": r.ID, "booking_code": r.BookingCode, "ticket_class_name": r.TcName,
			"buyer_name": r.BuyerName, "buyer_email": r.BuyerEmail, "event_name": r.EventName,
			"event_date": r.EventDate, "status": r.Status, "used_at": r.UsedAt, "created_at": r.CreatedAt,
		})
	}

	utils.Success(c, gin.H{"attendees": attendees})
}

func (h *SuperAdminHandler) GetOrder(c *gin.Context) {
	orderID := c.Param("orderId")

	type orderDetail struct {
		ID, EventID, TcID, BuyerID, BuyerName, BuyerEmail, BuyerPhone, Status, EventName, TcName, CreatedAt string
		Quantity  int
		TotalAmount float64
	}
	var o orderDetail
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT o.id, o.event_id, o.ticket_class_id, o.quantity, o.total_amount,
			o.buyer_name, o.buyer_email, o.buyer_phone, o.status, o.created_at,
			e.name as event_name, tc.name as ticket_class_name
		 FROM orders o
		 JOIN events e ON o.event_id = e.id
		 JOIN ticket_classes tc ON o.ticket_class_id = tc.id
		 WHERE o.id = $1`, orderID,
	).Scan(&o.ID, &o.EventID, &o.TcID, &o.Quantity, &o.TotalAmount,
		&o.BuyerName, &o.BuyerEmail, &o.BuyerPhone, &o.Status, &o.CreatedAt,
		&o.EventName, &o.TcName)
	if err != nil {
		utils.NotFound(c, "Order not found")
		return
	}

	utils.Success(c, gin.H{
		"order": gin.H{
			"id": o.ID, "event_id": o.EventID, "ticket_class_id": o.TcID,
			"quantity": o.Quantity, "total_amount": o.TotalAmount,
			"buyer_name": o.BuyerName, "buyer_email": o.BuyerEmail, "buyer_phone": o.BuyerPhone,
			"status": o.Status, "created_at": o.CreatedAt,
			"event_name": o.EventName, "ticket_class_name": o.TcName,
		},
	})
}

func (h *SuperAdminHandler) BulkCreateUsers(c *gin.Context) {
	var req struct {
		Users []struct {
			Email    string `json:"email"`
			FullName string `json:"fullName"`
			Role     string `json:"role"`
		} `json:"users"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "Invalid data")
		return
	}

	var created, failed int
	for _, u := range req.Users {
		_, err := h.pool.Exec(c.Request.Context(),
			`INSERT INTO users (email, full_name, role, is_verified)
			 VALUES ($1, $2, $3, true) ON CONFLICT (email) DO NOTHING`,
			u.Email, u.FullName, u.Role,
		)
		if err != nil {
			failed++
		} else {
			created++
		}
	}

	utils.Success(c, gin.H{"created": created, "failed": failed})
}

func (h *SuperAdminHandler) GetUser(c *gin.Context) {
	userID := c.Param("userId")

	type userRow struct {
		ID, Email, FullName, Role, CreatedAt string
		IsVerified bool
		ClassID *string
	}
	var u userRow
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT id, email, full_name, role, is_verified, class_id, created_at
		 FROM users WHERE id = $1`, userID,
	).Scan(&u.ID, &u.Email, &u.FullName, &u.Role, &u.IsVerified, &u.ClassID, &u.CreatedAt)
	if err != nil {
		utils.NotFound(c, "User not found")
		return
	}

	utils.Success(c, gin.H{
		"user": gin.H{
			"id": u.ID, "email": u.Email, "full_name": u.FullName, "role": u.Role,
			"is_verified": u.IsVerified, "class_id": u.ClassID, "created_at": u.CreatedAt,
		},
	})
}

// helper: read env var via os.Getenv (used by GetEnvStatus)
func osGetEnv(key string) string {
	return os.Getenv(key)
}
