package rolestore

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"aijianzhan/platform/internal/authx"
)

type Role struct {
	RoleID      int64     `json:"role_id"`
	TenantID    int64     `json:"tenant_id"`
	Code        string    `json:"code"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	Permissions []string  `json:"permissions"`
	CreatedAt   time.Time `json:"created_at"`
}

type CreateInput struct {
	Code        string
	Name        string
	Description string
	Permissions []string
}

type UpdateInput struct {
	Name        *string
	Description *string
	Permissions *[]string
}

type Store interface {
	List(ctx context.Context, tenantID int64) ([]Role, error)
	Get(ctx context.Context, tenantID, roleID int64) (*Role, error)
	Create(ctx context.Context, tenantID int64, in CreateInput) (*Role, error)
	Update(ctx context.Context, tenantID, roleID int64, in UpdateInput) (*Role, error)
	Delete(ctx context.Context, tenantID, roleID int64) error
	EnsureDefaults(ctx context.Context, tenantID int64) error
}

var defaultRoles = []CreateInput{
	{
		Code: authx.AgentRole生成发布, Name: "生成发布", Description: "生成蓝图、发布应用并导入业务数据（智能体常用最低权限）",
		Permissions: []string{
			"读取模块", "发布模块",
			"导入数据", "查询数据",
			"上传文件", "下载文件",
		},
	},
	{
		Code: authx.AgentRole只读, Name: "只读", Description: "读应用与数据，可导出，不可改写",
		Permissions: []string{"读取模块", "查询数据", "导出数据", "下载文件", "查看审计"},
	},
	{
		Code: authx.AgentRole读写, Name: "读写", Description: "读写业务数据与导入导出，不可发布模块",
		Permissions: []string{
			"读取模块", "写入模块",
			"新增数据", "查询数据", "更新数据", "导出数据", "导入数据",
			"上传文件", "下载文件",
		},
	},
	{
		Code: authx.AgentRole运维, Name: "运维", Description: "含发布、删除及全量行操作，适合运维类智能体",
		Permissions: []string{
			"读取模块", "写入模块", "发布模块",
			"新增数据", "查询数据", "更新数据", "删除数据", "导出数据", "导入数据",
			"上传文件", "下载文件", "查看审计",
		},
	},
}

type memRole struct{ Role }

type MemoryStore struct {
	mu  sync.Mutex
	seq int64
	by  map[int64]*memRole
}

func NewMemoryStore() *MemoryStore {
	return &MemoryStore{by: map[int64]*memRole{}}
}

func findDefaultRole(byCode map[string]Role, d CreateInput) (Role, bool) {
	if r, ok := byCode[d.Code]; ok {
		return r, true
	}
	legacy := map[string]string{
		"publisher": authx.AgentRole生成发布,
		"viewer":    authx.AgentRole只读,
		"editor":    authx.AgentRole读写,
		"operator":  authx.AgentRole运维,
	}
	for eng, zh := range legacy {
		if zh == d.Code {
			if r, ok := byCode[eng]; ok {
				return r, true
			}
		}
	}
	return Role{}, false
}

func (s *MemoryStore) EnsureDefaults(ctx context.Context, tenantID int64) error {
	list, err := s.List(ctx, tenantID)
	if err != nil {
		return err
	}
	byCode := map[string]Role{}
	for _, r := range list {
		byCode[r.Code] = r
	}
	for _, d := range defaultRoles {
		if existing, ok := findDefaultRole(byCode, d); ok {
			if d.Code == authx.AgentRole生成发布 {
				perms := append([]string{}, d.Permissions...)
				if _, err := s.Update(ctx, tenantID, existing.RoleID, UpdateInput{
					Name:        &d.Name,
					Description: &d.Description,
					Permissions: &perms,
				}); err != nil {
					return err
				}
			}
			continue
		}
		if _, err := s.Create(ctx, tenantID, d); err != nil {
			return err
		}
	}
	return nil
}

func (s *MemoryStore) List(_ context.Context, tenantID int64) ([]Role, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	out := []Role{}
	for _, r := range s.by {
		if r.TenantID == tenantID {
			out = append(out, clone(r.Role))
		}
	}
	return out, nil
}

func (s *MemoryStore) Get(_ context.Context, tenantID, roleID int64) (*Role, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	r, ok := s.by[roleID]
	if !ok || r.TenantID != tenantID {
		return nil, fmt.Errorf("role not found")
	}
	cp := clone(r.Role)
	return &cp, nil
}

func (s *MemoryStore) Create(_ context.Context, tenantID int64, in CreateInput) (*Role, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	code := strings.TrimSpace(in.Code)
	name := strings.TrimSpace(in.Name)
	if code == "" || name == "" {
		return nil, fmt.Errorf("code and name required")
	}
	for _, r := range s.by {
		if r.TenantID == tenantID && r.Code == code {
			return nil, fmt.Errorf("role code already exists")
		}
	}
	s.seq++
	r := &memRole{Role: Role{
		RoleID:      s.seq,
		TenantID:    tenantID,
		Code:        code,
		Name:        name,
		Description: strings.TrimSpace(in.Description),
		Permissions: uniq(in.Permissions),
		CreatedAt:   time.Now().UTC(),
	}}
	s.by[r.RoleID] = r
	cp := clone(r.Role)
	return &cp, nil
}

func (s *MemoryStore) Update(_ context.Context, tenantID, roleID int64, in UpdateInput) (*Role, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	r, ok := s.by[roleID]
	if !ok || r.TenantID != tenantID {
		return nil, fmt.Errorf("role not found")
	}
	if in.Name != nil {
		r.Name = strings.TrimSpace(*in.Name)
	}
	if in.Description != nil {
		r.Description = strings.TrimSpace(*in.Description)
	}
	if in.Permissions != nil {
		r.Permissions = uniq(*in.Permissions)
	}
	cp := clone(r.Role)
	return &cp, nil
}

func (s *MemoryStore) Delete(_ context.Context, tenantID, roleID int64) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	r, ok := s.by[roleID]
	if !ok || r.TenantID != tenantID {
		return fmt.Errorf("role not found")
	}
	delete(s.by, roleID)
	return nil
}

type PostgresStore struct{ DB *sql.DB }

func NewPostgresStore(db *sql.DB) *PostgresStore { return &PostgresStore{DB: db} }

func (s *PostgresStore) EnsureDefaults(ctx context.Context, tenantID int64) error {
	list, err := s.List(ctx, tenantID)
	if err != nil {
		return err
	}
	byCode := map[string]Role{}
	for _, r := range list {
		byCode[r.Code] = r
	}
	for _, d := range defaultRoles {
		if existing, ok := findDefaultRole(byCode, d); ok {
			// 英文旧编码升级为中文
			if existing.Code != d.Code {
				if _, err := s.DB.ExecContext(ctx, `UPDATE platform_meta.roles SET code=$1, name=$2 WHERE role_id=$3 AND tenant_id=$4`,
					d.Code, d.Name, existing.RoleID, tenantID); err != nil {
					return err
				}
				existing.Code = d.Code
				existing.Name = d.Name
				byCode[d.Code] = existing
			}
			if d.Code == authx.AgentRole生成发布 {
				perms := append([]string{}, d.Permissions...)
				if _, err := s.Update(ctx, tenantID, existing.RoleID, UpdateInput{
					Name:        &d.Name,
					Description: &d.Description,
					Permissions: &perms,
				}); err != nil {
					return err
				}
			}
			continue
		}
		if _, err := s.Create(ctx, tenantID, d); err != nil {
			return err
		}
	}
	return nil
}

func (s *PostgresStore) List(ctx context.Context, tenantID int64) ([]Role, error) {
	rows, err := s.DB.QueryContext(ctx, `
SELECT role_id, tenant_id, code, name, description, created_at
FROM platform_meta.roles WHERE tenant_id=$1 ORDER BY role_id`, tenantID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []Role
	for rows.Next() {
		var r Role
		if err := rows.Scan(&r.RoleID, &r.TenantID, &r.Code, &r.Name, &r.Description, &r.CreatedAt); err != nil {
			return nil, err
		}
		perms, err := s.loadPerms(ctx, r.RoleID)
		if err != nil {
			return nil, err
		}
		r.Permissions = perms
		out = append(out, r)
	}
	return out, rows.Err()
}

func (s *PostgresStore) Get(ctx context.Context, tenantID, roleID int64) (*Role, error) {
	var r Role
	err := s.DB.QueryRowContext(ctx, `
SELECT role_id, tenant_id, code, name, description, created_at
FROM platform_meta.roles WHERE role_id=$1 AND tenant_id=$2`, roleID, tenantID,
	).Scan(&r.RoleID, &r.TenantID, &r.Code, &r.Name, &r.Description, &r.CreatedAt)
	if errors.Is(err, sql.ErrNoRows) {
		return nil, fmt.Errorf("role not found")
	}
	if err != nil {
		return nil, err
	}
	perms, err := s.loadPerms(ctx, r.RoleID)
	if err != nil {
		return nil, err
	}
	r.Permissions = perms
	return &r, nil
}

func (s *PostgresStore) Create(ctx context.Context, tenantID int64, in CreateInput) (*Role, error) {
	code := strings.TrimSpace(in.Code)
	name := strings.TrimSpace(in.Name)
	if code == "" || name == "" {
		return nil, fmt.Errorf("code and name required")
	}
	var r Role
	err := s.DB.QueryRowContext(ctx, `
INSERT INTO platform_meta.roles(tenant_id, code, name, description)
VALUES($1,$2,$3,$4)
RETURNING role_id, tenant_id, code, name, description, created_at`,
		tenantID, code, name, strings.TrimSpace(in.Description),
	).Scan(&r.RoleID, &r.TenantID, &r.Code, &r.Name, &r.Description, &r.CreatedAt)
	if err != nil {
		return nil, err
	}
	perms := uniq(in.Permissions)
	if err := s.replacePerms(ctx, r.RoleID, perms); err != nil {
		return nil, err
	}
	r.Permissions = perms
	return &r, nil
}

func (s *PostgresStore) Update(ctx context.Context, tenantID, roleID int64, in UpdateInput) (*Role, error) {
	r, err := s.Get(ctx, tenantID, roleID)
	if err != nil {
		return nil, err
	}
	name, desc := r.Name, r.Description
	if in.Name != nil {
		name = strings.TrimSpace(*in.Name)
	}
	if in.Description != nil {
		desc = strings.TrimSpace(*in.Description)
	}
	if _, err := s.DB.ExecContext(ctx, `
UPDATE platform_meta.roles SET name=$1, description=$2 WHERE role_id=$3 AND tenant_id=$4`,
		name, desc, roleID, tenantID); err != nil {
		return nil, err
	}
	if in.Permissions != nil {
		if err := s.replacePerms(ctx, roleID, uniq(*in.Permissions)); err != nil {
			return nil, err
		}
	}
	return s.Get(ctx, tenantID, roleID)
}

func (s *PostgresStore) Delete(ctx context.Context, tenantID, roleID int64) error {
	_, _ = s.DB.ExecContext(ctx, `
UPDATE platform_meta.agent_accounts SET role_id=NULL WHERE role_id=$1 AND tenant_id=$2`, roleID, tenantID)
	res, err := s.DB.ExecContext(ctx, `
DELETE FROM platform_meta.roles WHERE role_id=$1 AND tenant_id=$2`, roleID, tenantID)
	if err != nil {
		return err
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return fmt.Errorf("role not found")
	}
	return nil
}

func (s *PostgresStore) loadPerms(ctx context.Context, roleID int64) ([]string, error) {
	rows, err := s.DB.QueryContext(ctx, `SELECT perm FROM platform_meta.role_permissions WHERE role_id=$1`, roleID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var out []string
	for rows.Next() {
		var p string
		if err := rows.Scan(&p); err != nil {
			return nil, err
		}
		out = append(out, p)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return authx.NormalizePerms(out), nil
}

func (s *PostgresStore) replacePerms(ctx context.Context, roleID int64, perms []string) error {
	tx, err := s.DB.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer func() { _ = tx.Rollback() }()
	if _, err := tx.ExecContext(ctx, `DELETE FROM platform_meta.role_permissions WHERE role_id=$1`, roleID); err != nil {
		return err
	}
	for _, p := range authx.NormalizePerms(perms) {
		if _, err := tx.ExecContext(ctx, `INSERT INTO platform_meta.role_permissions(role_id, perm) VALUES($1,$2)`, roleID, p); err != nil {
			return err
		}
	}
	return tx.Commit()
}

func clone(r Role) Role {
	cp := r
	cp.Permissions = append([]string{}, r.Permissions...)
	return cp
}

func uniq(in []string) []string {
	return authx.NormalizePerms(in)
}
