package store

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

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

// ValidRole 校验角色取值合法。
func ValidRole(role string) bool {
	switch role {
	case RoleOwner, RoleAdmin, RoleMember, RoleViewer, RoleBillingAdmin:
		return true
	}
	return false
}

// 角色/成员状态常量。
const (
	RoleOwner        = "owner"
	RoleAdmin        = "admin"
	RoleMember       = "member"
	RoleViewer       = "viewer"
	RoleBillingAdmin = "billing_admin"
)

// CreateTenant 建一个租户。
func (p *Postgres) CreateTenant(ctx context.Context, name, slug, plan string) (*Tenant, error) {
	if p.db == nil {
		return nil, errStoreDisabled
	}
	if plan == "" {
		plan = "free"
	}
	t := &Tenant{Name: name, Slug: slug, Plan: plan, Status: "active"}
	if err := p.db.WithContext(ctx).Create(t).Error; err != nil {
		return nil, err
	}
	return t, nil
}

// AddMember 添加成员（幂等：已存在则不重复插入）。
func (p *Postgres) AddMember(ctx context.Context, tenantID, userID, role string) error {
	if p.db == nil {
		return errStoreDisabled
	}
	m := &TenantMember{TenantID: tenantID, UserID: userID, Role: role, Status: "active"}
	// 命中唯一约束(tenant_id,user_id)则忽略，保证幂等。
	return p.db.WithContext(ctx).
		Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "tenant_id"}, {Name: "user_id"}}, DoNothing: true}).
		Create(m).Error
}

// DefaultTenantForUser 返回用户所属的默认租户（当前取其最早加入的 active 成员关系；无则 nil）。
// tenantIDForOwner 返回某 user 的默认(最早 active)租户 id；无则空串。
// 供异步路径（入库 / 评测回写，无请求 ctx 租户）在写入时补 tenant_id。
func (p *Postgres) tenantIDForOwner(ctx context.Context, owner string) string {
	if p.db == nil || owner == "" {
		return ""
	}
	var m TenantMember
	if err := p.db.WithContext(ctx).Select("tenant_id").
		Where("user_id = ? AND status = ?", owner, "active").
		Order("created_at asc").First(&m).Error; err != nil {
		return ""
	}
	return m.TenantID
}

func (p *Postgres) DefaultTenantForUser(ctx context.Context, userID string) (*Tenant, error) {
	if p.db == nil {
		return nil, errStoreDisabled
	}
	var m TenantMember
	err := p.db.WithContext(ctx).
		Where("user_id = ? AND status = ?", userID, "active").
		Order("created_at asc").First(&m).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	var t Tenant
	if err := p.db.WithContext(ctx).First(&t, "id = ?", m.TenantID).Error; err != nil {
		return nil, err
	}
	return &t, nil
}

// EnsureDefaultTenant 幂等地保证用户有一个默认租户：已有 active 成员关系则返回其默认租户；
// 否则建一个单人租户(owner) 并返回。用于注册与存量回填。
func (p *Postgres) EnsureDefaultTenant(ctx context.Context, userID, name string) (*Tenant, error) {
	if p.db == nil {
		return nil, errStoreDisabled
	}
	if t, err := p.DefaultTenantForUser(ctx, userID); err != nil {
		return nil, err
	} else if t != nil {
		return t, nil
	}
	if name == "" {
		name = "个人工作区"
	}
	t, err := p.CreateTenant(ctx, name, "default-"+userID, "free")
	if err != nil {
		return nil, err
	}
	if err := p.AddMember(ctx, t.ID, userID, RoleOwner); err != nil {
		return nil, err
	}
	return t, nil
}

// BackfillDefaultTenants 给所有还没有租户成员关系的存量用户补建默认租户（启动时幂等调用）。
func (p *Postgres) BackfillDefaultTenants(ctx context.Context) (int, error) {
	if p.db == nil {
		return 0, errStoreDisabled
	}
	var users []User
	// 无 active 成员关系的用户。
	if err := p.db.WithContext(ctx).
		Where("id NOT IN (?)", p.db.Model(&TenantMember{}).Select("user_id").Where("status = ?", "active")).
		Find(&users).Error; err != nil {
		return 0, err
	}
	n := 0
	for _, u := range users {
		if _, err := p.EnsureDefaultTenant(ctx, u.ID, firstNonEmpty(u.Name, u.Email)); err != nil {
			return n, err
		}
		n++
	}
	return n, nil
}

// GetTenant 按 id 取租户（不存在返回 nil）。
func (p *Postgres) GetTenant(ctx context.Context, id string) (*Tenant, error) {
	if p.db == nil {
		return nil, errStoreDisabled
	}
	var t Tenant
	if err := p.db.WithContext(ctx).First(&t, "id = ?", id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return &t, nil
}

// MemberRole 返回用户在某租户的角色（无成员关系返回空）。
func (p *Postgres) MemberRole(ctx context.Context, tenantID, userID string) string {
	if p.db == nil {
		return ""
	}
	var m TenantMember
	if err := p.db.WithContext(ctx).
		Where("tenant_id = ? AND user_id = ? AND status = ?", tenantID, userID, "active").
		First(&m).Error; err != nil {
		return ""
	}
	return m.Role
}

func firstNonEmpty(a, b string) string {
	if a != "" {
		return a
	}
	return b
}

// ---- 成员管理（多成员租户）----

// MemberInfo 是一条成员视图（join user 拿邮箱/名字）。
type MemberInfo struct {
	UserID   string    `json:"user_id"`
	Email    string    `json:"email"`
	Name     string    `json:"name"`
	Role     string    `json:"role"`
	Status   string    `json:"status"`
	JoinedAt time.Time `json:"joined_at"`
}

// ListMembers 返回某租户的成员（含邮箱/名字/角色），排除已移除。
func (p *Postgres) ListMembers(ctx context.Context, tenantID string) ([]MemberInfo, error) {
	if p.db == nil {
		return nil, nil
	}
	var out []MemberInfo
	err := p.db.WithContext(ctx).Table("sundynix_tenant_member m").
		Select("m.user_id, u.email, u.name, m.role, m.status, m.created_at as joined_at").
		Joins("JOIN sundynix_user u ON u.id = m.user_id").
		Where("m.tenant_id = ? AND m.status <> ?", tenantID, "removed").
		Order("m.created_at asc").Scan(&out).Error
	return out, err
}

// AddMemberByEmail 按邮箱把已注册用户加入租户（幂等：已在则改其角色并置 active，含"复活"已移除者）。
// 用户不存在返回错误（本阶段不做外发邀请邮件，须对方先注册）。
func (p *Postgres) AddMemberByEmail(ctx context.Context, tenantID, email, role string) (*MemberInfo, error) {
	if p.db == nil {
		return nil, errStoreDisabled
	}
	if !ValidRole(role) {
		return nil, errors.New("非法角色")
	}
	// 注册时邮箱按小写存，这里也归一化，避免大小写导致查不到。
	u, err := p.GetUserByEmail(ctx, strings.ToLower(strings.TrimSpace(email)))
	if errors.Is(err, gorm.ErrRecordNotFound) || u == nil {
		return nil, errors.New("该邮箱尚未注册，请对方先注册后再加入")
	}
	if err != nil {
		return nil, err
	}
	if err := p.db.WithContext(ctx).Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: "tenant_id"}, {Name: "user_id"}},
		DoUpdates: clause.Assignments(map[string]any{"role": role, "status": "active", "updated_at": time.Now()}),
	}).Create(&TenantMember{TenantID: tenantID, UserID: u.ID, Role: role, Status: "active"}).Error; err != nil {
		return nil, err
	}
	return &MemberInfo{UserID: u.ID, Email: u.Email, Name: u.Name, Role: role, Status: "active"}, nil
}

// SetMemberRole 改成员角色。不允许把 owner 降级（须先转让 owner，超出本阶段）。
func (p *Postgres) SetMemberRole(ctx context.Context, tenantID, userID, role string) error {
	if p.db == nil {
		return errStoreDisabled
	}
	if !ValidRole(role) {
		return errors.New("非法角色")
	}
	if p.MemberRole(ctx, tenantID, userID) == RoleOwner {
		return errors.New("不能改 owner 的角色")
	}
	if role == RoleOwner {
		return errors.New("不能直接授予 owner（须转让）")
	}
	return p.db.WithContext(ctx).Model(&TenantMember{}).
		Where("tenant_id = ? AND user_id = ?", tenantID, userID).Update("role", role).Error
}

// RemoveMember 软移除成员（status=removed）。不能移除 owner。
func (p *Postgres) RemoveMember(ctx context.Context, tenantID, userID string) error {
	if p.db == nil {
		return errStoreDisabled
	}
	if p.MemberRole(ctx, tenantID, userID) == RoleOwner {
		return errors.New("不能移除 owner")
	}
	return p.db.WithContext(ctx).Model(&TenantMember{}).
		Where("tenant_id = ? AND user_id = ?", tenantID, userID).Update("status", "removed").Error
}

// TenantInfo 是管理端租户目录一行（含成员数 + 余额）。
type TenantInfo struct {
	ID                 string `json:"id"`
	Name               string `json:"name"`
	Slug               string `json:"slug"`
	Plan               string `json:"plan"`
	Status             string `json:"status"`
	CreditBalanceMicro int64  `json:"credit_balance_micro"`
	SharedBilling      bool   `json:"shared_billing"`
	Members            int64  `json:"members"`
}

// ListTenants 返回全部租户（管理端目录：含 active 成员数 + 积分余额）。
func (p *Postgres) ListTenants(ctx context.Context) ([]TenantInfo, error) {
	if p.db == nil {
		return nil, nil
	}
	var out []TenantInfo
	err := p.db.WithContext(ctx).Table("sundynix_tenant t").
		Select("t.id, t.name, t.slug, t.plan, t.status, t.credit_balance_micro, t.shared_billing, "+
			"(SELECT count(*) FROM sundynix_tenant_member m WHERE m.tenant_id = t.id AND m.status = 'active') as members").
		Where("t.deleted_at IS NULL").Order("t.created_at asc").Scan(&out).Error
	return out, err
}

// MyTenants 返回某用户所属（active）的全部租户（供多租户切换）。
func (p *Postgres) MyTenants(ctx context.Context, userID string) ([]TenantInfo, error) {
	if p.db == nil {
		return nil, nil
	}
	var out []TenantInfo
	err := p.db.WithContext(ctx).Table("sundynix_tenant t").
		Select("t.id, t.name, t.slug, t.plan, t.status, t.credit_balance_micro, t.shared_billing, "+
			"(SELECT count(*) FROM sundynix_tenant_member m2 WHERE m2.tenant_id = t.id AND m2.status = 'active') as members").
		Joins("JOIN sundynix_tenant_member m ON m.tenant_id = t.id").
		Where("m.user_id = ? AND m.status = 'active' AND t.deleted_at IS NULL", userID).
		Order("t.created_at asc").Scan(&out).Error
	return out, err
}

// ---- 租户切换 + 共享计费（增量2）----

// PersonalTenantForUser 返回用户自己的个人租户（最早加入且角色 owner 的租户 = 注册默认租户）。
func (p *Postgres) PersonalTenantForUser(ctx context.Context, userID string) string {
	if p.db == nil {
		return ""
	}
	var m TenantMember
	if err := p.db.WithContext(ctx).Select("tenant_id").
		Where("user_id = ? AND status = 'active' AND role = ?", userID, RoleOwner).
		Order("created_at asc").First(&m).Error; err != nil {
		return ""
	}
	return m.TenantID
}

// ActiveTenantForUser 解析用户当前活跃租户：user.active_tenant_id 若已设且仍是 active 成员 → 用之；
// 否则回退默认租户（并保证默认租户存在）。中间件每请求调用。
func (p *Postgres) ActiveTenantForUser(ctx context.Context, userID string) (*Tenant, error) {
	def, err := p.EnsureDefaultTenant(ctx, userID, "")
	if err != nil {
		return nil, err
	}
	var u User
	if e := p.db.WithContext(ctx).Select("active_tenant_id").First(&u, "id = ?", userID).Error; e == nil {
		if u.ActiveTenantID != "" && u.ActiveTenantID != def.ID && p.MemberRole(ctx, u.ActiveTenantID, userID) != "" {
			if t, _ := p.GetTenant(ctx, u.ActiveTenantID); t != nil {
				return t, nil
			}
		}
	}
	return def, nil
}

// SetActiveTenant 切换用户活跃租户（须为其 active 成员）。
func (p *Postgres) SetActiveTenant(ctx context.Context, userID, tenantID string) error {
	if p.db == nil {
		return errStoreDisabled
	}
	if p.MemberRole(ctx, tenantID, userID) == "" {
		return errors.New("你不是该租户的成员")
	}
	return p.db.WithContext(ctx).Model(&User{}).Where("id = ?", userID).Update("active_tenant_id", tenantID).Error
}

// SetSharedBilling 设某租户共享计费开关。
func (p *Postgres) SetSharedBilling(ctx context.Context, tenantID string, on bool) error {
	if p.db == nil {
		return errStoreDisabled
	}
	return p.db.WithContext(ctx).Model(&Tenant{}).Where("id = ?", tenantID).Update("shared_billing", on).Error
}

// ResolveBillingTenantID 决定"这次消耗记谁的池子"：本人是 active 租户 owner，或 active 开了
// shared_billing → 记 active 租户；否则记本人个人租户（各付各的）。
func (p *Postgres) ResolveBillingTenantID(ctx context.Context, userID, activeTenantID string) string {
	if activeTenantID == "" {
		return ""
	}
	if p.MemberRole(ctx, activeTenantID, userID) == RoleOwner {
		return activeTenantID
	}
	if t, _ := p.GetTenant(ctx, activeTenantID); t != nil && t.SharedBilling {
		return activeTenantID
	}
	if personal := p.PersonalTenantForUser(ctx, userID); personal != "" {
		return personal
	}
	return activeTenantID
}
