package system

import (
	"strconv"

	"gorm.io/gorm"
)

// casbinRulePO mirrors the gorm-adapter CasbinRule storage shape. API
// metadata (sys_apis) and authorization policy are intentionally independent:
// a policy may refer to a route that has not been registered in sys_apis yet.
// Keep this type private so the storage representation does not leak above
// the data layer.
type casbinRulePO struct {
	ID    uint   `gorm:"primaryKey;autoIncrement"`
	Ptype string `gorm:"size:100"`
	V0    string `gorm:"size:100"`
	V1    string `gorm:"size:100"`
	V2    string `gorm:"size:100"`
	V3    string `gorm:"size:100"`
	V4    string `gorm:"size:100"`
	V5    string `gorm:"size:100"`
}

func (casbinRulePO) TableName() string { return "casbin_rule" }

func newPolicyRule(authorityID uint, path, method string) casbinRulePO {
	return casbinRulePO{
		Ptype: "p",
		V0:    strconv.FormatUint(uint64(authorityID), 10),
		V1:    path,
		V2:    method,
	}
}

func policyScope(db *gorm.DB) *gorm.DB {
	return db.Session(&gorm.Session{NewDB: true}).Model(&casbinRulePO{}).Where("ptype = ?", "p")
}

func deletePoliciesForAuthority(db *gorm.DB, authorityID uint) error {
	return policyScope(db).Where("v0 = ?", strconv.FormatUint(uint64(authorityID), 10)).Delete(&casbinRulePO{}).Error
}

func deletePoliciesForPath(db *gorm.DB, path, method string) error {
	return policyScope(db).Where("v1 = ? AND v2 = ?", path, method).Delete(&casbinRulePO{}).Error
}

func policyRowsForAuthority(db *gorm.DB, authorityID uint) ([]casbinRulePO, error) {
	var rows []casbinRulePO
	err := policyScope(db).Where("v0 = ?", strconv.FormatUint(uint64(authorityID), 10)).Find(&rows).Error
	return rows, err
}

func policyExists(db *gorm.DB, authorityID uint, path, method string) (bool, error) {
	var count int64
	err := policyScope(db).
		Where("v0 = ? AND v1 = ? AND v2 = ?", strconv.FormatUint(uint64(authorityID), 10), path, method).
		Count(&count).Error
	return count > 0, err
}
