package services

import (
	"context"
	"errors"
	"time"

	"quyun/v2/app/errorx"
	super_dto "quyun/v2/app/http/super/v1/dto"
	"quyun/v2/app/requests"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"

	"github.com/google/uuid"
	"github.com/spf13/cast"
	"go.ipao.vip/gen/types"
	"gorm.io/gorm"
)

// @provider
type super struct{}

func (s *super) Login(ctx context.Context, form *super_dto.LoginForm) (*super_dto.LoginResponse, error) {
	// TODO: Admin specific login or reuse User service
	return &super_dto.LoginResponse{}, nil
}

func (s *super) CheckToken(ctx context.Context) (*super_dto.LoginResponse, error) {
	return &super_dto.LoginResponse{}, nil
}

func (s *super) ListUsers(ctx context.Context, filter *super_dto.UserListFilter) (*requests.Pager, error) {
	tbl, q := models.UserQuery.QueryContext(ctx)
	if filter.Username != nil && *filter.Username != "" {
		q = q.Where(tbl.Username.Like("%" + *filter.Username + "%")).Or(tbl.Nickname.Like("%" + *filter.Username + "%"))
	}

	filter.Pagination.Format()
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	var data []super_dto.UserItem
	for _, u := range list {
		data = append(data, super_dto.UserItem{
			SuperUserLite: super_dto.SuperUserLite{
				ID:                u.ID,
				Username:          u.Username,
				Roles:             u.Roles,
				Status:            u.Status,
				StatusDescription: u.Status.Description(),
				CreatedAt:         u.CreatedAt.Format(time.RFC3339),
				UpdatedAt:         u.UpdatedAt.Format(time.RFC3339),
			},
			Balance:       u.Balance,
			BalanceFrozen: u.BalanceFrozen,
		})
	}

	return &requests.Pager{
		Pagination: filter.Pagination,
		Total:      total,
		Items:      data,
	}, nil
}

func (s *super) GetUser(ctx context.Context, id int64) (*super_dto.UserItem, error) {
	tbl, q := models.UserQuery.QueryContext(ctx)
	u, err := q.Where(tbl.ID.Eq(id)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errorx.ErrRecordNotFound
		}
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	return &super_dto.UserItem{
		SuperUserLite: super_dto.SuperUserLite{
			ID:                u.ID,
			Username:          u.Username,
			Roles:             u.Roles,
			Status:            u.Status,
			StatusDescription: u.Status.Description(),
			CreatedAt:         u.CreatedAt.Format(time.RFC3339),
			UpdatedAt:         u.UpdatedAt.Format(time.RFC3339),
		},
		Balance:       u.Balance,
		BalanceFrozen: u.BalanceFrozen,
	}, nil
}

func (s *super) UpdateUserStatus(ctx context.Context, id int64, form *super_dto.UserStatusUpdateForm) error {
	tbl, q := models.UserQuery.QueryContext(ctx)
	_, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Status, consts.UserStatus(form.Status))
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *super) UpdateUserRoles(ctx context.Context, id int64, form *super_dto.UserRolesUpdateForm) error {
	var roles types.Array[consts.Role]
	for _, r := range form.Roles {
		roles = append(roles, r)
	}
	tbl, q := models.UserQuery.QueryContext(ctx)
	_, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Roles, roles)
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *super) ListTenants(ctx context.Context, filter *super_dto.TenantListFilter) (*requests.Pager, error) {
	tbl, q := models.TenantQuery.QueryContext(ctx)
	if filter.Name != nil && *filter.Name != "" {
		q = q.Where(tbl.Name.Like("%" + *filter.Name + "%"))
	}

	filter.Pagination.Format()
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	var data []super_dto.TenantItem
	for _, t := range list {
		data = append(data, super_dto.TenantItem{
			ID:                t.ID,
			UUID:              t.UUID.String(),
			Name:              t.Name,
			Code:              t.Code,
			Status:            t.Status,
			StatusDescription: t.Status.Description(),
			UserID:            t.UserID,
			CreatedAt:         t.CreatedAt.Format(time.RFC3339),
			UpdatedAt:         t.UpdatedAt.Format(time.RFC3339),
		})
	}

	return &requests.Pager{
		Pagination: filter.Pagination,
		Total:      total,
		Items:      data,
	}, nil
}

func (s *super) CreateTenant(ctx context.Context, form *super_dto.TenantCreateForm) error {
	uid := cast.ToInt64(form.AdminUserID)
	if _, err := models.UserQuery.WithContext(ctx).Where(models.UserQuery.ID.Eq(uid)).First(); err != nil {
		return errorx.ErrRecordNotFound.WithMsg("用户不存在")
	}

	t := &models.Tenant{
		UserID: uid,
		Name:   form.Name,
		Code:   form.Code,
		UUID:   types.UUID(uuid.New()),
		Status: consts.TenantStatusVerified,
	}
	if err := models.TenantQuery.WithContext(ctx).Create(t); err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *super) GetTenant(ctx context.Context, id int64) (*super_dto.TenantItem, error) {
	tbl, q := models.TenantQuery.QueryContext(ctx)
	t, err := q.Where(tbl.ID.Eq(id)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errorx.ErrRecordNotFound
		}
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	return &super_dto.TenantItem{
		ID:                t.ID,
		UUID:              t.UUID.String(),
		Name:              t.Name,
		Code:              t.Code,
		Status:            t.Status,
		StatusDescription: t.Status.Description(),
		UserID:            t.UserID,
		CreatedAt:         t.CreatedAt.Format(time.RFC3339),
		UpdatedAt:         t.UpdatedAt.Format(time.RFC3339),
	}, nil
}

func (s *super) UpdateTenantStatus(ctx context.Context, id int64, form *super_dto.TenantStatusUpdateForm) error {
	tbl, q := models.TenantQuery.QueryContext(ctx)
	_, err := q.Where(tbl.ID.Eq(id)).Update(tbl.Status, consts.TenantStatus(form.Status))
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *super) UpdateTenantExpire(ctx context.Context, id int64, form *super_dto.TenantExpireUpdateForm) error {
	expire := time.Now().AddDate(0, 0, form.Duration)
	tbl, q := models.TenantQuery.QueryContext(ctx)
	_, err := q.Where(tbl.ID.Eq(id)).Update(tbl.ExpiredAt, expire)
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *super) ListContents(ctx context.Context, filter *super_dto.SuperContentListFilter) (*requests.Pager, error) {
	tbl, q := models.ContentQuery.QueryContext(ctx)

	filter.Pagination.Format()
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	// Simplified DTO for list
	var data []any
	for _, c := range list {
		data = append(data, c) // TODO: Map to DTO
	}
	return &requests.Pager{
		Pagination: filter.Pagination,
		Total:      total,
		Items:      data,
	}, nil
}

func (s *super) UpdateContentStatus(ctx context.Context, tenantID, contentID int64, form *super_dto.SuperTenantContentStatusUpdateForm) error {
	tbl, q := models.ContentQuery.QueryContext(ctx)
	_, err := q.Where(tbl.ID.Eq(contentID), tbl.TenantID.Eq(tenantID)).Update(tbl.Status, consts.ContentStatus(form.Status))
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *super) ListOrders(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
	tbl, q := models.OrderQuery.QueryContext(ctx)

	filter.Pagination.Format()
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	// TODO: Map to DTO
	return &requests.Pager{
		Pagination: filter.Pagination,
		Total:      total,
		Items:      list,
	}, nil
}

func (s *super) GetOrder(ctx context.Context, id int64) (*super_dto.SuperOrderDetail, error) {
	return &super_dto.SuperOrderDetail{}, nil
}

func (s *super) RefundOrder(ctx context.Context, id int64, form *super_dto.SuperOrderRefundForm) error {
	return nil
}

func (s *super) OrderStatistics(ctx context.Context) (*super_dto.OrderStatisticsResponse, error) {
	return &super_dto.OrderStatisticsResponse{}, nil
}

func (s *super) UserStatistics(ctx context.Context) ([]super_dto.UserStatistics, error) {
	return []super_dto.UserStatistics{}, nil
}

func (s *super) UserStatuses(ctx context.Context) ([]requests.KV, error) {
	return consts.UserStatusItems(), nil
}

func (s *super) TenantStatuses(ctx context.Context) ([]requests.KV, error) {
	return consts.TenantStatusItems(), nil
}

func (s *super) ListWithdrawals(ctx context.Context, filter *super_dto.SuperOrderListFilter) (*requests.Pager, error) {
	tbl, q := models.OrderQuery.QueryContext(ctx)
	q = q.Where(tbl.Type.Eq(consts.OrderTypeWithdrawal))

	filter.Pagination.Format()
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Order(tbl.ID.Desc()).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	// TODO: Map to SuperOrderItem properly with Tenant/User lookup
	return &requests.Pager{
		Pagination: filter.Pagination,
		Total:      total,
		Items:      list,
	}, nil
}

func (s *super) ApproveWithdrawal(ctx context.Context, id int64) error {
	o, err := models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(id)).First()
	if err != nil {
		return errorx.ErrRecordNotFound
	}
	if o.Status != consts.OrderStatusCreated {
		return errorx.ErrStatusConflict.WithMsg("订单状态不正确")
	}

	// Mark as Paid (Assumes external transfer done)
	_, err = models.OrderQuery.WithContext(ctx).Where(models.OrderQuery.ID.Eq(id)).Updates(&models.Order{
		Status:    consts.OrderStatusPaid,
		PaidAt:    time.Now(),
		UpdatedAt: time.Now(),
	})
	if err == nil && Audit != nil {
		Audit.Log(ctx, "approve_withdrawal", cast.ToString(id), "Approved withdrawal")
	}
	return err
}

func (s *super) RejectWithdrawal(ctx context.Context, id int64, reason string) error {
	err := models.Q.Transaction(func(tx *models.Query) error {
		o, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).First()
		if err != nil {
			return errorx.ErrRecordNotFound
		}
		if o.Status != consts.OrderStatusCreated {
			return errorx.ErrStatusConflict.WithMsg("订单状态不正确")
		}

		// Refund User Balance
		_, err = tx.User.WithContext(ctx).Where(tx.User.ID.Eq(o.UserID)).Update(tx.User.Balance, gorm.Expr("balance + ?", o.AmountPaid))
		if err != nil {
			return err
		}

		// Update Order
		_, err = tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(id)).Updates(&models.Order{
			Status:       consts.OrderStatusFailed, // or Canceled
			RefundReason: reason,
			UpdatedAt:    time.Now(),
		})
		if err != nil {
			return err
		}

		// Create Ledger (Adjustment/Unfreeze)
		ledger := &models.TenantLedger{
			TenantID:       o.TenantID,
			UserID:         o.UserID,
			OrderID:        o.ID,
			Type:           consts.TenantLedgerTypeAdjustment,
			Amount:         o.AmountPaid,
			Remark:         "提现拒绝返还: " + reason,
			OperatorUserID: 0, // System/Admin
			IdempotencyKey: uuid.NewString(),
		}
		if err := tx.TenantLedger.WithContext(ctx).Create(ledger); err != nil {
			return err
		}

		return nil
	})

	if err == nil && Audit != nil {
		Audit.Log(ctx, "reject_withdrawal", cast.ToString(id), "Rejected: "+reason)
	}
	return err
}
