package listing

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"hfb_sys/backend/internal/model"

	"gorm.io/datatypes"
	"gorm.io/gorm"
)

func (r *Repository) Create(ctx context.Context, ownerID uint64, req CreateRequest, reviewRequired bool) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listingNo, err := r.nextListingNo(tx, time.Now())
		if err != nil {
			return err
		}
		screenshots, err := marshalScreenshots(req.ScreenshotURLS)
		if err != nil {
			return err
		}
		assetSummary, err := marshalAssetSummary(req.AssetSummary)
		if err != nil {
			return err
		}
		listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
		account := model.GameAccount{
			OwnerID:        ownerID,
			GameName:       "delta_force",
			ServerRegion:   req.ServerRegion,
			LoginPlatform:  req.LoginPlatform,
			Title:          req.Title,
			Description:    req.Description,
			RankLevel:      req.RankLevel,
			HafCoinAmount:  req.HafCoinAmount,
			AssetSummary:   assetSummary,
			ScreenshotURLS: screenshots,
			Status:         listingStatus,
		}
		if err := tx.Create(&account).Error; err != nil {
			return err
		}
		priceCent := normalizedListingPriceCent(req)
		listing := model.RentalListing{
			ListingNo:         listingNo,
			AccountID:         account.ID,
			OwnerID:           ownerID,
			PriceCent:         priceCent,
			DepositAmountCent: req.DepositAmountCent,
			Status:            listingStatus,
			ReviewStatus:      reviewStatus,
			PublishedAt:       publishedAt,
		}
		if err := tx.Create(&listing).Error; err != nil {
			return err
		}

		// 发布提交时建发布群
		var conversationID uint64
		if r.chatCreator != nil {
			convID, err := r.chatCreator.EnsureListingConversation(tx, listing, 0)
			if err != nil {
				return err
			}
			conversationID = convID
		}

		dto = toDTO(account, listing)
		dto.ListingGroupConversationID = conversationID
		return nil
	})
	return dto, err
}

type externalUploadCreate struct {
	UploaderName     string
	ClientUploadTime *time.Time
	ClientIP         string
	RawPayload       []byte
	ParsedPayload    []byte
}

func (r *Repository) CreateFromExternalUpload(ctx context.Context, upload externalUploadCreate, req CreateRequest) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listingNo, err := r.nextListingNo(tx, time.Now())
		if err != nil {
			return err
		}
		admin, err := r.findActiveUploadAdmin(tx, upload.UploaderName)
		if err != nil {
			return err
		}
		owner, err := r.ensureUploadOwnerUser(tx, admin)
		if err != nil {
			return err
		}
		screenshots, err := marshalScreenshots(req.ScreenshotURLS)
		if err != nil {
			return err
		}
		assetSummary, err := marshalAssetSummary(req.AssetSummary)
		if err != nil {
			return err
		}
		account := model.GameAccount{
			OwnerID:        owner.ID,
			GameName:       "delta_force",
			ServerRegion:   req.ServerRegion,
			LoginPlatform:  req.LoginPlatform,
			Title:          req.Title,
			Description:    req.Description,
			RankLevel:      req.RankLevel,
			HafCoinAmount:  req.HafCoinAmount,
			AssetSummary:   assetSummary,
			ScreenshotURLS: screenshots,
			Status:         "draft",
		}
		if err := tx.Create(&account).Error; err != nil {
			return err
		}
		priceCent := normalizedListingPriceCent(req)
		listing := model.RentalListing{
			ListingNo:         listingNo,
			AccountID:         account.ID,
			OwnerID:           owner.ID,
			PriceCent:         priceCent,
			DepositAmountCent: req.DepositAmountCent,
			Status:            "draft",
			ReviewStatus:      "pending",
		}
		if err := tx.Create(&listing).Error; err != nil {
			return err
		}
		var conversationID uint64
		if r.chatCreator != nil {
			convID, err := r.chatCreator.EnsureListingConversation(tx, listing, admin.ID)
			if err != nil {
				return err
			}
			conversationID = convID
		}
		matchedAdminID := admin.ID
		ownerID := owner.ID
		listingID := listing.ID
		uploadRow := model.ListingUpload{
			UploaderName:     upload.UploaderName,
			MatchedAdminID:   &matchedAdminID,
			OwnerID:          &ownerID,
			ClientUploadTime: upload.ClientUploadTime,
			ClientIP:         upload.ClientIP,
			RawPayload:       datatypes.JSON(upload.RawPayload),
			ParsedPayload:    datatypes.JSON(upload.ParsedPayload),
			ListingID:        &listingID,
			Status:           "draft_created",
		}
		if err := tx.Create(&uploadRow).Error; err != nil {
			return err
		}
		dto = toDTO(account, listing)
		dto.ListingGroupConversationID = conversationID
		return nil
	})
	return dto, err
}

func (r *Repository) Update(ctx context.Context, ownerID uint64, listingID uint64, req UpdateRequest, reviewRequired bool) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}

		account.Title = req.Title
		account.Description = req.Description
		account.ServerRegion = req.ServerRegion
		account.LoginPlatform = req.LoginPlatform
		account.RankLevel = req.RankLevel
		account.HafCoinAmount = req.HafCoinAmount
		assetSummary, err := marshalAssetSummary(req.AssetSummary)
		if err != nil {
			return err
		}
		account.AssetSummary = assetSummary
		screenshots, err := marshalScreenshots(req.ScreenshotURLS)
		if err != nil {
			return err
		}
		account.ScreenshotURLS = screenshots

		listing.PriceCent = normalizedListingPriceCent(req)
		listing.DepositAmountCent = req.DepositAmountCent
		listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
		listing.Status = listingStatus
		listing.ReviewStatus = reviewStatus
		listing.ReviewReason = ""
		listing.PublishedAt = publishedAt
		account.Status = listingStatus
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}

func (r *Repository) SubmitReview(ctx context.Context, ownerID uint64, listingID uint64, reviewRequired bool) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}
		listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
		listing.Status = listingStatus
		listing.ReviewStatus = reviewStatus
		listing.ReviewReason = ""
		listing.PublishedAt = publishedAt
		account.Status = listingStatus
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}

func (r *Repository) findActiveUploadAdmin(tx *gorm.DB, uploaderName string) (*model.AdminUser, error) {
	uploaderName = strings.TrimSpace(uploaderName)
	var admin model.AdminUser
	if err := tx.Where("username = ? AND status = ?", uploaderName, "active").First(&admin).Error; err == nil {
		return &admin, nil
	} else if !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	var admins []model.AdminUser
	if err := tx.Where("nickname = ? AND status = ?", uploaderName, "active").Limit(2).Find(&admins).Error; err != nil {
		return nil, err
	}
	switch len(admins) {
	case 0:
		return nil, ErrUploaderNotFound
	case 1:
		return &admins[0], nil
	default:
		return nil, ErrUploaderAmbiguous
	}
}

func (r *Repository) ensureUploadOwnerUser(tx *gorm.DB, admin *model.AdminUser) (*model.User, error) {
	phone := fmt.Sprintf("admin:%d", admin.ID)
	nickname := strings.TrimSpace(admin.Nickname)
	if nickname == "" {
		nickname = admin.Username
	}
	var user model.User
	err := tx.Where("phone = ?", phone).First(&user).Error
	if err == nil {
		updates := map[string]any{
			"nickname":        nickname,
			"status":          "active",
			"realname_status": "verified",
		}
		if err := tx.Model(&user).Updates(updates).Error; err != nil {
			return nil, err
		}
		user.Nickname = nickname
		user.Status = "active"
		user.RealnameStatus = "verified"
		return &user, nil
	}
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	user = model.User{
		Phone:          phone,
		Nickname:       nickname,
		RealnameStatus: "verified",
		RiskStatus:     "normal",
		CreditScore:    100,
		Status:         "active",
	}
	if err := tx.Create(&user).Error; err != nil {
		return nil, err
	}
	return &user, nil
}

func (r *Repository) Offline(ctx context.Context, ownerID uint64, listingID uint64) (*ListingDTO, error) {
	var dto *ListingDTO
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
		if err != nil {
			return err
		}
		if listing.Status == "rented" || listing.InTransaction {
			return ErrListingLocked
		}
		listing.Status = "offline"
		listing.ReviewStatus = "none"
		listing.ReviewReason = "号主已手动下架"
		listing.PublishedAt = nil
		account.Status = "offline"
		if err := tx.Save(account).Error; err != nil {
			return err
		}
		if err := tx.Save(listing).Error; err != nil {
			return err
		}
		dto = toDTO(*account, *listing)
		return nil
	})
	return dto, err
}
