package listing

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"hfb_sys/backend/internal/model"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func (r *Repository) ListAdmin(ctx context.Context, query AdminListQuery) (*AdminListResult, error) {
	page := query.Page
	if page <= 0 {
		page = 1
	}
	pageSize := query.PageSize
	if pageSize <= 0 {
		pageSize = query.Limit
	}
	if pageSize <= 0 {
		pageSize = 20
	}
	if pageSize > 100 {
		pageSize = 100
	}

	countDB := r.applyAdminListFilters(r.db.WithContext(ctx).Table("rental_listings AS l"), query)
	var total int64
	if err := countDB.Count(&total).Error; err != nil {
		return nil, err
	}

	db := r.applyAdminListFilters(r.baseQuery(ctx), query)
	offset := (page - 1) * pageSize
	if offset < 0 {
		offset = 0
	}
	var rows []listingRow
	err := db.Order("l.id DESC").Limit(pageSize).Offset(offset).Scan(&rows).Error
	if err != nil {
		return nil, err
	}
	return &AdminListResult{
		Items:    rowsToDTO(rows),
		Total:    total,
		Page:     page,
		PageSize: pageSize,
	}, nil
}

func (r *Repository) applyAdminListFilters(db *gorm.DB, query AdminListQuery) *gorm.DB {
	if query.OwnerID > 0 {
		db = db.Where("l.owner_id = ?", query.OwnerID)
	}
	if query.Status != "" {
		db = db.Where("l.status = ?", query.Status)
	}
	if query.ReviewStatus != "" {
		db = db.Where("l.review_status = ?", query.ReviewStatus)
	}
	return db
}

func (r *Repository) FindAdmin(ctx context.Context, listingID uint64) (*ListingDTO, error) {
	return r.findDTO(ctx, "l.id = ?", listingID)
}

func (r *Repository) ListMine(ctx context.Context, ownerID uint64) ([]ListingDTO, error) {
	var rows []listingRow
	err := r.baseQuery(ctx).
		Where("l.owner_id = ?", ownerID).
		Order("l.id DESC").
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}
	return sellerListings(rowsToDTO(rows)), nil
}

func (r *Repository) FindMine(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
	dto, err := r.findDTO(ctx, "l.id = ? AND l.owner_id = ?", id, ownerID)
	if err != nil {
		return nil, err
	}
	applySellerListingPrice(dto)
	return dto, nil
}

func (r *Repository) findOwnedForUpdate(tx *gorm.DB, ownerID uint64, listingID uint64) (*model.RentalListing, *model.GameAccount, error) {
	var listing model.RentalListing
	if err := tx.Where("id = ? AND owner_id = ?", listingID, ownerID).First(&listing).Error; err != nil {
		return nil, nil, err
	}
	var account model.GameAccount
	if err := tx.Where("id = ? AND owner_id = ?", listing.AccountID, ownerID).First(&account).Error; err != nil {
		return nil, nil, err
	}
	return &listing, &account, nil
}

func (r *Repository) findDTO(ctx context.Context, where string, args ...any) (*ListingDTO, error) {
	var row listingRow
	err := r.baseQuery(ctx).
		Where(where, args...).
		First(&row).Error
	if err != nil {
		return nil, err
	}
	dto := row.toDTO()
	return &dto, nil
}

func (r *Repository) baseQuery(ctx context.Context) *gorm.DB {
	return r.db.WithContext(ctx).Table("rental_listings AS l").
		Select(`l.*, a.title, a.description, a.game_name, a.server_region, a.login_platform, a.rank_level,
			a.haf_coin_amount, a.asset_summary, a.screenshot_urls, COALESCE(u.phone, '') AS owner_phone, COALESCE(u.nickname, '') AS owner_nickname`).
		Joins("JOIN game_accounts AS a ON a.id = l.account_id").
		Joins("LEFT JOIN users AS u ON u.id = l.owner_id")
}

func (r *Repository) findForReviewUpdate(tx *gorm.DB, listingID uint64) (*model.RentalListing, *model.GameAccount, error) {
	var listing model.RentalListing
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, listingID).Error; err != nil {
		return nil, nil, err
	}
	var account model.GameAccount
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, listing.AccountID).Error; err != nil {
		return nil, nil, err
	}
	return &listing, &account, nil
}

func (r *Repository) nextListingNo(tx *gorm.DB, now time.Time) (string, error) {
	bizDate := now.Format("20060102")
	if tx.Dialector.Name() == "mysql" {
		if err := tx.Exec(`
			INSERT INTO listing_no_sequences (biz_date, next_seq)
			VALUES (?, LAST_INSERT_ID(1))
			ON DUPLICATE KEY UPDATE next_seq = LAST_INSERT_ID(next_seq + 1)
		`, bizDate).Error; err != nil {
			return "", err
		}
		var seq int
		if err := tx.Raw("SELECT LAST_INSERT_ID()").Scan(&seq).Error; err != nil {
			return "", err
		}
		return fmt.Sprintf("%s%04d", bizDate, seq), nil
	}

	var maxNo string
	if err := tx.Table("rental_listings").
		Select("COALESCE(MAX(listing_no), '')").
		Where("listing_no LIKE ?", bizDate+"%").
		Scan(&maxNo).Error; err != nil {
		return "", err
	}
	seq := 1
	if len(maxNo) > len(bizDate) {
		if parsed, err := strconv.Atoi(maxNo[len(bizDate):]); err == nil {
			seq = parsed + 1
		}
	}
	return fmt.Sprintf("%s%04d", bizDate, seq), nil
}
