package model

import (
	"errors"
	"strconv"
	"strings"
	"time"

	"github.com/QuantumNous/new-api/common"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

const (
	// SupplierApplicationStatusPending 表示待审核。
	SupplierApplicationStatusPending = 0
	// SupplierApplicationStatusApproved 表示审核通过。
	SupplierApplicationStatusApproved = 1
	// SupplierApplicationStatusRejected 表示审核驳回。
	SupplierApplicationStatusRejected = 2
	// SupplierApplicationStatusDeactivated 表示供应商已注销。
	SupplierApplicationStatusDeactivated = 3
)

const (
	// SupplierApplicationAuditActionSubmit 表示提交申请。
	SupplierApplicationAuditActionSubmit = 0
	// SupplierApplicationAuditActionApprove 表示审核通过。
	SupplierApplicationAuditActionApprove = 1
	// SupplierApplicationAuditActionReject 表示审核驳回。
	SupplierApplicationAuditActionReject = 2
	// SupplierApplicationAuditActionDeactivate 表示供应商注销。
	SupplierApplicationAuditActionDeactivate = 3
	// SupplierApplicationAuditActionActivate 表示供应商重新启用。
	SupplierApplicationAuditActionActivate = 4
)

const (
	// UserMessageBizTypeSupplierApplication 供应商申请业务类型。
	UserMessageBizTypeSupplierApplication = "supplier_application"
	// UserMessageTypeSupplierSubmitted 供应商提交待审核消息。
	UserMessageTypeSupplierSubmitted = "supplier_submitted"
	// UserMessageTypeSupplierApproved 供应商审核通过消息。
	UserMessageTypeSupplierApproved = "supplier_approved"
	// UserMessageTypeSupplierRejected 供应商审核驳回消息。
	UserMessageTypeSupplierRejected = "supplier_rejected"
)

var (
	// ErrSupplierApplicationAlreadyReviewed 表示申请已被其他管理员处理。
	ErrSupplierApplicationAlreadyReviewed = errors.New("supplier application already reviewed")
	// ErrSupplierApplicationStatusNotEditable 表示申请当前状态不可修改（已审核通过）。
	ErrSupplierApplicationStatusNotEditable = errors.New("supplier application status is not editable")
	// ErrSupplierApplicationStatusNotApproved 表示申请当前不处于审核通过状态，不能执行供应商注销。
	ErrSupplierApplicationStatusNotApproved = errors.New("supplier application status is not approved")
	// ErrSupplierApplicationStatusNotDeactivated 表示申请当前不处于已注销状态，不能执行供应商启用。
	ErrSupplierApplicationStatusNotDeactivated = errors.New("supplier application status is not deactivated")
)

// SupplierApplication 供应商入驻申请主表。
type SupplierApplication struct {
	ID                  int                 `json:"id" gorm:"primaryKey;comment:主键ID"`
	ApplicantUserID     int                 `json:"applicant_user_id" gorm:"index;not null;comment:申请人用户ID"`
	ApplicantUsername   string              `json:"applicant_username" gorm:"column:applicant_username;->;comment:申请人用户名（关联 users.username）"`
	ApplicantQuota      int                 `json:"applicant_quota" gorm:"column:applicant_quota;->;comment:申请人账户剩余额度（列表联表回填）"`
	ApplicantUsedQuota  int                 `json:"applicant_used_quota" gorm:"column:applicant_used_quota;->;comment:申请人历史累计已用额度（列表联表回填）"`
	CompanyName         string              `json:"company_name" gorm:"type:varchar(255);not null;comment:企业或主体名称"`
	CreditCode          string              `json:"credit_code" gorm:"type:varchar(32);not null;uniqueIndex;comment:统一社会信用代码"`
	BusinessLicenseURL  string              `json:"business_license_url" gorm:"type:varchar(1024);not null;comment:营业执照文件URL"`
	BusinessLicenseFile string              `json:"business_license_file" gorm:"type:varchar(255);not null;default:'';comment:营业执照文件名称"`
	CompanyLogoURL      string              `json:"company_logo_url" gorm:"type:varchar(1024);not null;default:'';comment:企业Logo图片URL"`
	SupplierType        string              `json:"supplier_type" gorm:"type:varchar(64);not null;default:'';comment:供应商类型"`
	LegalRepresentative string              `json:"legal_representative" gorm:"type:varchar(128);not null;comment:法人或经营者姓名"`
	CompanySize         string              `json:"company_size" gorm:"type:varchar(64);comment:企业规模"`
	ContactName         string              `json:"contact_name" gorm:"type:varchar(128);not null;comment:对接人姓名"`
	ContactMobile       string              `json:"contact_mobile" gorm:"type:varchar(32);not null;comment:对接人手机号"`
	ContactWechat       string              `json:"contact_wechat" gorm:"type:varchar(128);not null;comment:对接人微信或企业微信"`
	SupplierAlias       *string             `json:"supplier_alias" gorm:"type:varchar(128);uniqueIndex;comment:供应商别名，创建时默认 P+主键 id，可修改"`
	SupplierCapability  *SupplierCapability `json:"supplier_capability" gorm:"-"`
	Status              int                 `json:"status" gorm:"type:int;index;default:0;not null;comment:审核状态 0待审核 1已通过 2已驳回 3已注销"`
	ReviewReason        string              `json:"review_reason" gorm:"type:text;comment:审核备注或驳回原因"`
	ReviewedBy          int                 `json:"reviewed_by" gorm:"type:int;index;default:0;comment:审核人用户ID"`
	ReviewedAt          int64               `json:"reviewed_at" gorm:"type:bigint;default:0;comment:审核时间戳"`
	CreatedAt           int64               `json:"created_at" gorm:"type:bigint;index;comment:创建时间戳"`
	UpdatedAt           int64               `json:"updated_at" gorm:"type:bigint;comment:更新时间戳"`
}

// SupplierApplicationAudit 供应商审核极简审计表。
type SupplierApplicationAudit struct {
	ID             int    `json:"id" gorm:"primaryKey;comment:主键ID"`
	ApplicationID  int    `json:"application_id" gorm:"index;not null;comment:供应商申请ID"`
	OperatorUserID int    `json:"operator_user_id" gorm:"index;not null;comment:操作人用户ID"`
	Action         int    `json:"action" gorm:"type:int;index;not null;comment:操作类型 0提交 1通过 2驳回"`
	FromStatus     int    `json:"from_status" gorm:"type:int;not null;comment:变更前状态"`
	ToStatus       int    `json:"to_status" gorm:"type:int;not null;comment:变更后状态"`
	Reason         string `json:"reason" gorm:"type:text;comment:审核备注或驳回原因"`
	CreatedAt      int64  `json:"created_at" gorm:"type:bigint;index;comment:创建时间戳"`
}

// UserMessage 通用站内消息表（支持按用户与按角色广播）。
type UserMessage struct {
	ID              int    `json:"id" gorm:"primaryKey;comment:主键ID"`
	ReceiverUserID  int    `json:"receiver_user_id" gorm:"type:int;index;default:0;comment:接收用户ID 0表示广播"`
	ReceiverMinRole int    `json:"receiver_min_role" gorm:"type:int;index;default:0;comment:广播最小角色门槛"`
	Type            string `json:"type" gorm:"type:varchar(64);index;comment:消息类型"`
	Title           string `json:"title" gorm:"type:varchar(255);not null;comment:消息标题"`
	Content         string `json:"content" gorm:"type:text;not null;comment:消息内容"`
	BizType         string `json:"biz_type" gorm:"type:varchar(64);index;comment:业务类型"`
	BizID           int    `json:"biz_id" gorm:"type:int;index;default:0;comment:业务ID"`
	IsRead          bool   `json:"is_read" gorm:"type:boolean;index;default:false;comment:是否已读"`
	ReadAt          int64  `json:"read_at" gorm:"type:bigint;default:0;comment:已读时间戳"`
	CreatedAt       int64  `json:"created_at" gorm:"type:bigint;index;comment:创建时间戳"`
}

// UserMessageRead 站内广播消息的用户已读记录。
// 仅用于 receiver_user_id=0 的广播消息按用户追踪已读状态。
type UserMessageRead struct {
	ID        int   `json:"id" gorm:"primaryKey;comment:主键ID"`
	UserID    int   `json:"user_id" gorm:"type:int;not null;uniqueIndex:idx_user_message_reads_user_message,priority:1;comment:用户ID"`
	MessageID int   `json:"message_id" gorm:"type:int;not null;uniqueIndex:idx_user_message_reads_user_message,priority:2;comment:消息ID"`
	ReadAt    int64 `json:"read_at" gorm:"type:bigint;not null;default:0;comment:已读时间戳"`
	CreatedAt int64 `json:"created_at" gorm:"type:bigint;index;comment:创建时间戳"`
}

// SupplierSimplePricingItem pricing 接口使用的供应商精简信息。
type SupplierSimplePricingItem struct {
	SupplierID   int    `json:"supplier_id"`
	SupplierName string `json:"supplier_name"`
}

// SupplierApplicationAutoAlias 供应商库内编号：P + 主键 id（创建/编辑后由服务端写入，无需前端传入）。
func SupplierApplicationAutoAlias(id int) string {
	if id <= 0 {
		return ""
	}
	return "P" + strconv.Itoa(id)
}

func ptrSupplierApplicationAlias(id int) *string {
	if id <= 0 {
		return nil
	}
	s := SupplierApplicationAutoAlias(id)
	return &s
}

// CreateSupplierApplication 创建供应商申请记录。
func CreateSupplierApplication(app *SupplierApplication) error {
	now := time.Now().Unix()
	app.CreatedAt = now
	app.UpdatedAt = now
	if err := DB.Create(app).Error; err != nil {
		return err
	}
	alias := SupplierApplicationAutoAlias(app.ID)
	if err := DB.Model(app).Update("supplier_alias", alias).Error; err != nil {
		return err
	}
	app.SupplierAlias = ptrSupplierApplicationAlias(app.ID)
	return nil
}

// CreateSupplierApplicationAutoApproved 创建直接审核通过的供应商申请。
// 该方法用于管理员及以上角色提交申请时，保证申请创建、用户绑定与审计记录在同一事务内完成。
func CreateSupplierApplicationAutoApproved(app *SupplierApplication, reviewerUserID int) error {
	tx := DB.Begin()
	if tx.Error != nil {
		return tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	now := time.Now().Unix()
	app.Status = SupplierApplicationStatusApproved
	app.ReviewedBy = reviewerUserID
	app.ReviewedAt = now
	app.ReviewReason = ""
	app.CreatedAt = now
	app.UpdatedAt = now
	if err := tx.Create(app).Error; err != nil {
		tx.Rollback()
		return err
	}
	alias := SupplierApplicationAutoAlias(app.ID)
	if err := tx.Model(&SupplierApplication{}).Where("id = ?", app.ID).Update("supplier_alias", alias).Error; err != nil {
		tx.Rollback()
		return err
	}
	app.SupplierAlias = ptrSupplierApplicationAlias(app.ID)
	if err := tx.Model(&User{}).Where("id = ?", app.ApplicantUserID).
		Updates(map[string]any{
			"supplier_id": app.ID,
		}).Error; err != nil {
		tx.Rollback()
		return err
	}
	if err := tx.Create(&SupplierApplicationAudit{
		ApplicationID:  app.ID,
		OperatorUserID: app.ApplicantUserID,
		Action:         SupplierApplicationAuditActionSubmit,
		FromStatus:     SupplierApplicationStatusPending,
		ToStatus:       SupplierApplicationStatusPending,
		Reason:         "",
		CreatedAt:      now,
	}).Error; err != nil {
		tx.Rollback()
		return err
	}
	if err := tx.Create(&SupplierApplicationAudit{
		ApplicationID:  app.ID,
		OperatorUserID: reviewerUserID,
		Action:         SupplierApplicationAuditActionApprove,
		FromStatus:     SupplierApplicationStatusPending,
		ToStatus:       SupplierApplicationStatusApproved,
		Reason:         "管理员提交自动通过",
		CreatedAt:      now,
	}).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

// CreateSupplierApplicationAudit 创建供应商审核审计记录。
func CreateSupplierApplicationAudit(audit *SupplierApplicationAudit) error {
	audit.CreatedAt = time.Now().Unix()
	return DB.Create(audit).Error
}

// ListSupplierApplications 分页查询供应商申请（管理员）。
func ListSupplierApplications(status *int, pageInfo *common.PageInfo) ([]*SupplierApplication, int64, error) {
	var (
		items []*SupplierApplication
		total int64
	)
	query := DB.Model(&SupplierApplication{})
	if status != nil {
		query = query.Where("status = ?", *status)
	}
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("id desc").
		Limit(pageInfo.GetPageSize()).
		Offset(pageInfo.GetStartIdx()).
		Find(&items).Error; err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

// ListSupplierApplicationsByApplicant 分页查询当前用户提交的申请。
func ListSupplierApplicationsByApplicant(applicantUserID int, status *int, pageInfo *common.PageInfo) ([]*SupplierApplication, int64, error) {
	var (
		items []*SupplierApplication
		total int64
	)
	query := DB.Model(&SupplierApplication{}).Where("applicant_user_id = ?", applicantUserID)
	if status != nil {
		query = query.Where("status = ?", *status)
	}
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("id desc").
		Limit(pageInfo.GetPageSize()).
		Offset(pageInfo.GetStartIdx()).
		Find(&items).Error; err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

// ListSuppliersByCompanyName 分页查询供应商列表（支持按供应商名称模糊搜索与状态筛选）。
func ListSuppliersByCompanyName(companyName string, statuses []int, pageInfo *common.PageInfo) ([]*SupplierApplication, int64, error) {
	var (
		items []*SupplierApplication
		total int64
	)
	query := DB.Model(&SupplierApplication{}).
		Joins("LEFT JOIN users ON users.id = supplier_applications.applicant_user_id")
	if len(statuses) > 0 {
		query = query.Where("supplier_applications.status IN ?", statuses)
	}
	if strings.TrimSpace(companyName) != "" {
		query = query.Where("company_name LIKE ?", "%"+strings.TrimSpace(companyName)+"%")
	}
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.
		Select("supplier_applications.*, users.username AS applicant_username, COALESCE(users.quota,0) AS applicant_quota, COALESCE(users.used_quota,0) AS applicant_used_quota").
		Order("supplier_applications.id desc").
		Limit(pageInfo.GetPageSize()).
		Offset(pageInfo.GetStartIdx()).
		Find(&items).Error; err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

// GetSupplierByID 根据供应商申请ID查询供应商详情（含申请人用户名）。
func GetSupplierByID(supplierID int) (*SupplierApplication, error) {
	var item SupplierApplication
	err := DB.Model(&SupplierApplication{}).
		Select("supplier_applications.*, users.username AS applicant_username, COALESCE(users.quota,0) AS applicant_quota, COALESCE(users.used_quota,0) AS applicant_used_quota").
		Joins("LEFT JOIN users ON users.id = supplier_applications.applicant_user_id").
		Where("supplier_applications.id = ?", supplierID).
		First(&item).Error
	if err != nil {
		return nil, err
	}
	return &item, nil
}

// GetMySupplierApplication 获取当前用户最近一条供应商申请（按ID倒序）。
func GetMySupplierApplication(applicantUserID int) (*SupplierApplication, error) {
	var app SupplierApplication
	err := DB.Where("applicant_user_id = ?", applicantUserID).
		Order("id desc").
		First(&app).Error
	if err != nil {
		return nil, err
	}
	return &app, nil
}

// UpdateMySupplierApplication 修改当前用户指定ID申请（仅已通过状态不可修改）。
func UpdateMySupplierApplication(applicantUserID int, applicationID int, req *SupplierApplication) (*SupplierApplication, error) {
	tx := DB.Begin()
	if tx.Error != nil {
		return nil, tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var app SupplierApplication
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("applicant_user_id = ? AND id = ?", applicantUserID, applicationID).
		First(&app).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if app.Status == SupplierApplicationStatusApproved {
		tx.Rollback()
		return nil, ErrSupplierApplicationStatusNotEditable
	}
	fromStatus := app.Status
	now := time.Now().Unix()
	updates := map[string]any{
		"company_name":          req.CompanyName,
		"credit_code":           req.CreditCode,
		"business_license_url":  req.BusinessLicenseURL,
		"business_license_file": req.BusinessLicenseFile,
		"company_logo_url":      req.CompanyLogoURL,
		"legal_representative":  req.LegalRepresentative,
		"company_size":          req.CompanySize,
		"contact_name":          req.ContactName,
		"contact_mobile":        req.ContactMobile,
		"contact_wechat":        req.ContactWechat,
		"status":                SupplierApplicationStatusPending,
		"review_reason":         "",
		"reviewed_by":           0,
		"reviewed_at":           0,
		"updated_at":            now,
	}
	if err := tx.Model(&SupplierApplication{}).
		Where("id = ?", app.ID).
		Updates(updates).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if err := tx.Create(&SupplierApplicationAudit{
		ApplicationID:  app.ID,
		OperatorUserID: applicantUserID,
		Action:         SupplierApplicationAuditActionSubmit,
		FromStatus:     fromStatus,
		ToStatus:       SupplierApplicationStatusPending,
		Reason:         "申请资料已修改并重新提交",
		CreatedAt:      now,
	}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	app.CompanyName = req.CompanyName
	app.CreditCode = req.CreditCode
	app.BusinessLicenseURL = req.BusinessLicenseURL
	app.BusinessLicenseFile = req.BusinessLicenseFile
	app.CompanyLogoURL = req.CompanyLogoURL
	app.LegalRepresentative = req.LegalRepresentative
	app.CompanySize = req.CompanySize
	app.ContactName = req.ContactName
	app.ContactMobile = req.ContactMobile
	app.ContactWechat = req.ContactWechat
	app.Status = SupplierApplicationStatusPending
	app.ReviewReason = ""
	app.ReviewedBy = 0
	app.ReviewedAt = 0
	app.UpdatedAt = now
	if err := tx.Commit().Error; err != nil {
		return nil, err
	}
	return &app, nil

}

// AdminUpdateSupplierApplication 管理员更新指定供应商申请资料。
// 与用户侧不同：即使申请处于审核通过状态，也允许管理员更新；更新后保持原状态不变。
func AdminUpdateSupplierApplication(applicationID int, req *SupplierApplication) (*SupplierApplication, error) {
	tx := DB.Begin()
	if tx.Error != nil {
		return nil, tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var app SupplierApplication
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("id = ?", applicationID).
		First(&app).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	now := time.Now().Unix()
	aliasVal := ""
	if req.SupplierAlias != nil {
		aliasVal = strings.TrimSpace(*req.SupplierAlias)
	}
	if aliasVal == "" {
		aliasVal = SupplierApplicationAutoAlias(applicationID)
	}
	updates := map[string]any{
		"company_name":          req.CompanyName,
		"credit_code":           req.CreditCode,
		"business_license_url":  req.BusinessLicenseURL,
		"business_license_file": req.BusinessLicenseFile,
		"company_logo_url":      req.CompanyLogoURL,
		"supplier_type":         req.SupplierType,
		"legal_representative":  req.LegalRepresentative,
		"company_size":          req.CompanySize,
		"contact_name":          req.ContactName,
		"contact_mobile":        req.ContactMobile,
		"contact_wechat":        req.ContactWechat,
		"supplier_alias":        aliasVal,
		"updated_at":            now,
	}
	if err := tx.Model(&SupplierApplication{}).
		Where("id = ?", app.ID).
		Updates(updates).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	app.CompanyName = req.CompanyName
	app.CreditCode = req.CreditCode
	app.BusinessLicenseURL = req.BusinessLicenseURL
	app.BusinessLicenseFile = req.BusinessLicenseFile
	app.CompanyLogoURL = req.CompanyLogoURL
	app.SupplierType = req.SupplierType
	app.LegalRepresentative = req.LegalRepresentative
	app.CompanySize = req.CompanySize
	app.ContactName = req.ContactName
	app.ContactMobile = req.ContactMobile
	app.ContactWechat = req.ContactWechat
	savedAlias := aliasVal
	app.SupplierAlias = &savedAlias
	app.UpdatedAt = now
	if err := tx.Commit().Error; err != nil {
		return nil, err
	}
	return &app, nil
}

// ListApprovedSuppliersForPricing 查询定价页使用的已审核通过供应商列表。
func ListApprovedSuppliersForPricing() ([]SupplierSimplePricingItem, error) {
	items := make([]SupplierSimplePricingItem, 0)
	err := DB.Model(&SupplierApplication{}).
		Select("id as supplier_id, company_name as supplier_name").
		Where("status = ?", SupplierApplicationStatusApproved).
		Order("id desc").
		Scan(&items).Error
	if err != nil {
		return nil, err
	}
	return items, nil
}

// GetApprovedSupplierApplicationByApplicant 获取当前用户的审核通过供应商申请。
func GetApprovedSupplierApplicationByApplicant(applicantUserID int) (*SupplierApplication, error) {
	var app SupplierApplication
	err := DB.Where("applicant_user_id = ? AND status = ?", applicantUserID, SupplierApplicationStatusApproved).
		Order("id desc").
		First(&app).Error
	if err != nil {
		return nil, err
	}
	return &app, nil
}

// ReviewSupplierApplication 审核申请（仅允许从待审核状态流转到通过/驳回）。
// supplierAlias 为可选：通过时若为空则写入默认 P+id，否则写入管理员指定别名。
func ReviewSupplierApplication(applicationID int, reviewerUserID int, toStatus int, reason string, supplierAlias string, supplierType string) (*SupplierApplication, error) {
	tx := DB.Begin()
	if tx.Error != nil {
		return nil, tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	var app SupplierApplication
	if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("id = ?", applicationID).
		First(&app).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if app.Status != SupplierApplicationStatusPending {
		tx.Rollback()
		return nil, ErrSupplierApplicationAlreadyReviewed
	}

	now := time.Now().Unix()
	updates := map[string]any{
		"status":        toStatus,
		"review_reason": reason,
		"reviewed_by":   reviewerUserID,
		"reviewed_at":   now,
		"updated_at":    now,
	}
	if toStatus == SupplierApplicationStatusApproved {
		trimmedAlias := strings.TrimSpace(supplierAlias)
		trimmedType := strings.TrimSpace(supplierType)
		if trimmedAlias != "" {
			updates["supplier_alias"] = trimmedAlias
		} else {
			updates["supplier_alias"] = SupplierApplicationAutoAlias(applicationID)
		}
		updates["supplier_type"] = trimmedType
	}
	result := tx.Model(&SupplierApplication{}).
		Where("id = ? AND status = ?", applicationID, SupplierApplicationStatusPending).
		Updates(updates)
	if result.Error != nil {
		tx.Rollback()
		return nil, result.Error
	}
	if result.RowsAffected == 0 {
		tx.Rollback()
		return nil, ErrSupplierApplicationAlreadyReviewed
	}

	action := SupplierApplicationAuditActionApprove
	if toStatus == SupplierApplicationStatusRejected {
		action = SupplierApplicationAuditActionReject
	}
	if err := tx.Create(&SupplierApplicationAudit{
		ApplicationID:  app.ID,
		OperatorUserID: reviewerUserID,
		Action:         action,
		FromStatus:     SupplierApplicationStatusPending,
		ToStatus:       toStatus,
		Reason:         reason,
		CreatedAt:      now,
	}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if toStatus == SupplierApplicationStatusApproved {
		// 审核通过后，回填用户表 supplier_id，建立用户与供应商关联。
		if err := tx.Model(&User{}).Where("id = ?", app.ApplicantUserID).
			Updates(map[string]any{
				"supplier_id": app.ID,
			}).Error; err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	app.Status = toStatus
	app.ReviewReason = reason
	app.ReviewedBy = reviewerUserID
	app.ReviewedAt = now
	app.UpdatedAt = now
	if toStatus == SupplierApplicationStatusApproved {
		trimmedAlias := strings.TrimSpace(supplierAlias)
		trimmedType := strings.TrimSpace(supplierType)
		if trimmedAlias != "" {
			app.SupplierAlias = &trimmedAlias
		} else {
			app.SupplierAlias = ptrSupplierApplicationAlias(applicationID)
		}
		app.SupplierType = trimmedType
	}
	if err := tx.Commit().Error; err != nil {
		return nil, err
	}
	return &app, nil
}

// DeactivateSupplierApplication 注销供应商（仅允许审核通过状态）。
// 管理员（role>=RoleAdminUser）可按ID注销任意供应商；普通用户仅可注销自己提交的供应商。
// 注销后会将 supplier_applications.status 置为已注销，并清空申请人用户表 supplier_id。
func DeactivateSupplierApplication(operatorUserID int, operatorRole int, supplierID int, reason string) (*SupplierApplication, error) {
	tx := DB.Begin()
	if tx.Error != nil {
		return nil, tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var app SupplierApplication
	query := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", supplierID)
	if operatorRole < common.RoleAdminUser {
		query = query.Where("applicant_user_id = ?", operatorUserID)
	}
	if err := query.First(&app).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if app.Status != SupplierApplicationStatusApproved {
		tx.Rollback()
		return nil, ErrSupplierApplicationStatusNotApproved
	}
	now := time.Now().Unix()
	if err := tx.Model(&SupplierApplication{}).
		Where("id = ?", app.ID).
		Updates(map[string]any{
			"status":        SupplierApplicationStatusDeactivated,
			"review_reason": strings.TrimSpace(reason),
			"updated_at":    now,
		}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if err := tx.Model(&User{}).Where("id = ?", app.ApplicantUserID).
		Updates(map[string]any{
			"supplier_id": 0,
		}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if err := tx.Create(&SupplierApplicationAudit{
		ApplicationID:  app.ID,
		OperatorUserID: operatorUserID,
		Action:         SupplierApplicationAuditActionDeactivate,
		FromStatus:     SupplierApplicationStatusApproved,
		ToStatus:       SupplierApplicationStatusDeactivated,
		Reason:         strings.TrimSpace(reason),
		CreatedAt:      now,
	}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	app.Status = SupplierApplicationStatusDeactivated
	app.ReviewReason = strings.TrimSpace(reason)
	app.UpdatedAt = now
	if err := tx.Commit().Error; err != nil {
		return nil, err
	}
	return &app, nil
}

// ActivateSupplierApplication 重新启用已注销供应商（仅允许已注销状态）。
// 管理员（role>=RoleAdminUser）可按ID启用任意供应商；普通用户仅可启用自己提交的供应商。
// 启用后会将 supplier_applications.status 置为审核通过，并回填申请人用户表 supplier_id。
func ActivateSupplierApplication(operatorUserID int, operatorRole int, supplierID int, reason string) (*SupplierApplication, error) {
	tx := DB.Begin()
	if tx.Error != nil {
		return nil, tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	var app SupplierApplication
	query := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", supplierID)
	if operatorRole < common.RoleAdminUser {
		query = query.Where("applicant_user_id = ?", operatorUserID)
	}
	if err := query.First(&app).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if app.Status != SupplierApplicationStatusDeactivated {
		tx.Rollback()
		return nil, ErrSupplierApplicationStatusNotDeactivated
	}
	now := time.Now().Unix()
	trimmedReason := strings.TrimSpace(reason)
	if err := tx.Model(&SupplierApplication{}).
		Where("id = ?", app.ID).
		Updates(map[string]any{
			"status":        SupplierApplicationStatusApproved,
			"review_reason": trimmedReason,
			"updated_at":    now,
		}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if err := tx.Model(&User{}).Where("id = ?", app.ApplicantUserID).
		Updates(map[string]any{
			"supplier_id": app.ID,
		}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	if err := tx.Create(&SupplierApplicationAudit{
		ApplicationID:  app.ID,
		OperatorUserID: operatorUserID,
		Action:         SupplierApplicationAuditActionActivate,
		FromStatus:     SupplierApplicationStatusDeactivated,
		ToStatus:       SupplierApplicationStatusApproved,
		Reason:         trimmedReason,
		CreatedAt:      now,
	}).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	app.Status = SupplierApplicationStatusApproved
	app.ReviewReason = trimmedReason
	app.UpdatedAt = now
	if err := tx.Commit().Error; err != nil {
		return nil, err
	}
	return &app, nil
}

// CreateUserMessage 创建站内消息。
func CreateUserMessage(msg *UserMessage) error {
	msg.CreatedAt = time.Now().Unix()
	return DB.Create(msg).Error
}

// ListUserMessagesForUser 分页查询当前用户可见消息。
// readStatus: all/read/unread；titleKeyword: 标题模糊查询关键字。
func ListUserMessagesForUser(userID int, role int, pageInfo *common.PageInfo, titleKeyword string, readStatus string) ([]*UserMessage, int64, error) {
	var (
		items []*UserMessage
		total int64
	)
	query := DB.Model(&UserMessage{}).Where("receiver_user_id = ? OR (receiver_user_id = 0 AND receiver_min_role > 0 AND receiver_min_role <= ?)", userID, role)
	if strings.TrimSpace(titleKeyword) != "" {
		query = query.Where("title LIKE ?", "%"+strings.TrimSpace(titleKeyword)+"%")
	}
	if readStatus == "read" {
		query = query.Where("(receiver_user_id = ? AND is_read = ?) OR (receiver_user_id = 0 AND EXISTS (SELECT 1 FROM user_message_reads umr WHERE umr.message_id = user_messages.id AND umr.user_id = ?))", userID, true, userID)
	} else if readStatus == "unread" {
		query = query.Where("(receiver_user_id = ? AND is_read = ?) OR (receiver_user_id = 0 AND NOT EXISTS (SELECT 1 FROM user_message_reads umr WHERE umr.message_id = user_messages.id AND umr.user_id = ?))", userID, false, userID)
	}
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := query.Order("id desc").
		Limit(pageInfo.GetPageSize()).
		Offset(pageInfo.GetStartIdx()).
		Find(&items).Error; err != nil {
		return nil, 0, err
	}
	broadcastIDs := make([]int, 0)
	for _, item := range items {
		if item.ReceiverUserID == 0 {
			broadcastIDs = append(broadcastIDs, item.ID)
		}
	}
	if len(broadcastIDs) > 0 {
		var readRows []UserMessageRead
		if err := DB.Model(&UserMessageRead{}).
			Select("message_id").
			Where("user_id = ? AND message_id IN ?", userID, broadcastIDs).
			Find(&readRows).Error; err != nil {
			return nil, 0, err
		}
		readMap := make(map[int]bool, len(readRows))
		for _, row := range readRows {
			readMap[row.MessageID] = true
		}
		for _, item := range items {
			if item.ReceiverUserID == 0 {
				item.IsRead = readMap[item.ID]
			}
		}
	}
	return items, total, nil
}

// CountUnreadUserMessages 统计当前用户未读消息（支持广播消息按用户已读追踪）。
func CountUnreadUserMessages(userID int, role int) (int64, error) {
	var (
		directTotal    int64
		broadcastTotal int64
	)
	if err := DB.Model(&UserMessage{}).
		Where("receiver_user_id = ? AND is_read = ?", userID, false).
		Count(&directTotal).Error; err != nil {
		return 0, err
	}
	if err := DB.Model(&UserMessage{}).
		Where("receiver_user_id = 0 AND receiver_min_role > 0 AND receiver_min_role <= ? AND NOT EXISTS (SELECT 1 FROM user_message_reads umr WHERE umr.message_id = user_messages.id AND umr.user_id = ?)", role, userID).
		Count(&broadcastTotal).Error; err != nil {
		return 0, err
	}
	return directTotal + broadcastTotal, nil
}

// MarkUserMessageAsRead 将用户可见消息标记为已读。
// 定向消息更新 user_messages.is_read；广播消息写入 user_message_reads 用户已读记录。
func MarkUserMessageAsRead(messageID int, userID int, role int) (bool, error) {
	var msg UserMessage
	if err := DB.Where("id = ? AND (receiver_user_id = ? OR (receiver_user_id = 0 AND receiver_min_role > 0 AND receiver_min_role <= ?))", messageID, userID, role).
		First(&msg).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return false, nil
		}
		return false, err
	}
	now := time.Now().Unix()
	if msg.ReceiverUserID == userID {
		res := DB.Model(&UserMessage{}).
			Where("id = ? AND receiver_user_id = ? AND is_read = ?", messageID, userID, false).
			Updates(map[string]any{
				"is_read": true,
				"read_at": now,
			})
		if res.Error != nil {
			return false, res.Error
		}
		return res.RowsAffected > 0, nil
	}
	read := UserMessageRead{
		UserID:    userID,
		MessageID: messageID,
		ReadAt:    now,
		CreatedAt: now,
	}
	res := DB.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "user_id"}, {Name: "message_id"}},
		DoUpdates: clause.Assignments(map[string]any{"read_at": now}),
	}).Create(&read)
	if res.Error != nil {
		return false, res.Error
	}
	return true, nil
}

// MarkAllUserMessagesAsRead 将当前用户可见消息全部标记为已读。
// 定向消息写回 user_messages；广播消息写入 user_message_reads。
func MarkAllUserMessagesAsRead(userID int, role int) (int64, error) {
	tx := DB.Begin()
	if tx.Error != nil {
		return 0, tx.Error
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	now := time.Now().Unix()
	res := tx.Model(&UserMessage{}).
		Where("receiver_user_id = ? AND is_read = ?", userID, false).
		Updates(map[string]any{
			"is_read": true,
			"read_at": now,
		})
	if res.Error != nil {
		tx.Rollback()
		return 0, res.Error
	}
	updatedCount := res.RowsAffected
	var broadcastIDs []int
	if err := tx.Model(&UserMessage{}).
		Select("id").
		Where("receiver_user_id = 0 AND receiver_min_role > 0 AND receiver_min_role <= ?", role).
		Find(&broadcastIDs).Error; err != nil {
		tx.Rollback()
		return 0, err
	}
	if len(broadcastIDs) > 0 {
		reads := make([]UserMessageRead, 0, len(broadcastIDs))
		for _, messageID := range broadcastIDs {
			reads = append(reads, UserMessageRead{
				UserID:    userID,
				MessageID: messageID,
				ReadAt:    now,
				CreatedAt: now,
			})
		}
		insertRes := tx.Clauses(clause.OnConflict{
			Columns:   []clause.Column{{Name: "user_id"}, {Name: "message_id"}},
			DoUpdates: clause.Assignments(map[string]any{"read_at": now}),
		}).Create(&reads)
		if insertRes.Error != nil {
			tx.Rollback()
			return 0, insertRes.Error
		}
		updatedCount += insertRes.RowsAffected
	}
	if err := tx.Commit().Error; err != nil {
		return 0, err
	}
	return updatedCount, nil
}

// IsSupplierApplicationNotFound 判断是否未找到供应商申请记录。
func IsSupplierApplicationNotFound(err error) bool {
	return errors.Is(err, gorm.ErrRecordNotFound)
}

// BackfillSupplierApplicationAlias 将 supplier_alias 统一为 P+id（迁移/修复用，可安全重复执行）。
func BackfillSupplierApplicationAlias() error {
	switch {
	case common.UsingMySQL:
		return DB.Exec("UPDATE supplier_applications SET supplier_alias = CONCAT('P', id) WHERE id > 0").Error
	case common.UsingPostgreSQL:
		return DB.Exec(`UPDATE supplier_applications SET supplier_alias = 'P' || id::text WHERE id > 0`).Error
	default:
		return DB.Exec("UPDATE supplier_applications SET supplier_alias = 'P' || id WHERE id > 0").Error
	}
}

// IsSupplierCreditCodeDuplicateError 判断是否为统一社会信用代码重复错误。
func IsSupplierCreditCodeDuplicateError(err error) bool {
	if err == nil {
		return false
	}
	lowerMsg := strings.ToLower(err.Error())
	if !strings.Contains(lowerMsg, "credit_code") {
		return false
	}
	// 兼容 MySQL / PostgreSQL / SQLite 常见唯一约束错误文案
	return strings.Contains(lowerMsg, "duplicate") ||
		strings.Contains(lowerMsg, "duplicated") ||
		strings.Contains(lowerMsg, "unique constraint") ||
		strings.Contains(lowerMsg, "unique failed") ||
		strings.Contains(lowerMsg, "idx_supplier_applications_credit_code")
}

// IsSupplierAliasDuplicateError 判断是否为供应商别名重复错误。
func IsSupplierAliasDuplicateError(err error) bool {
	if err == nil {
		return false
	}
	lowerMsg := strings.ToLower(err.Error())
	if !strings.Contains(lowerMsg, "supplier_alias") {
		return false
	}
	// 兼容 MySQL / PostgreSQL / SQLite 常见唯一约束错误文案
	return strings.Contains(lowerMsg, "duplicate") ||
		strings.Contains(lowerMsg, "duplicated") ||
		strings.Contains(lowerMsg, "unique constraint") ||
		strings.Contains(lowerMsg, "unique failed") ||
		strings.Contains(lowerMsg, "idx_supplier_applications_supplier_alias")
}
