package system

import (
	"context"
	"errors"
	"strings"

	"kra/internal/biz/system"
	"kra/internal/data/model"
	"kra/internal/data/query"

	"gorm.io/gorm"
)

type apiRepo struct {
	db *gorm.DB
}

// NewApiRepo 创建API仓储
func NewApiRepo(db *gorm.DB) system.ApiRepo {
	return &apiRepo{db: db}
}

func (r *apiRepo) Create(ctx context.Context, api *system.Api) error {
	_, err := query.SysAPI.WithContext(ctx).Where(
		query.SysAPI.Path.Eq(api.Path),
		query.SysAPI.Method.Eq(api.Method),
	).First()
	if err == nil {
		return errors.New("存在相同api")
	}
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}
	m := toModelAPI(api)
	return query.SysAPI.WithContext(ctx).Create(m)
}

func (r *apiRepo) Update(ctx context.Context, api *system.Api) error {
	old, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(api.ID))).First()
	if err != nil {
		return err
	}
	if old.Path != api.Path || old.Method != api.Method {
		dup, ferr := query.SysAPI.WithContext(ctx).Where(
			query.SysAPI.Path.Eq(api.Path),
			query.SysAPI.Method.Eq(api.Method),
		).First()
		if ferr == nil && dup.ID != int64(api.ID) {
			return errors.New("存在相同api路径")
		}
		if ferr != nil && !errors.Is(ferr, gorm.ErrRecordNotFound) {
			return ferr
		}
	}
	m := toModelAPI(api)
	_, err = query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(api.ID))).Updates(m)
	return err
}

func (r *apiRepo) Delete(ctx context.Context, id uint) error {
	_, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(id))).Delete()
	return err
}

func (r *apiRepo) DeleteByIds(ctx context.Context, ids []uint) error {
	int64Ids := make([]int64, len(ids))
	for i, id := range ids {
		int64Ids[i] = int64(id)
	}
	_, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.In(int64Ids...)).Delete()
	return err
}

func (r *apiRepo) FindByID(ctx context.Context, id uint) (*system.Api, error) {
	m, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.Eq(int64(id))).First()
	if err != nil {
		return nil, err
	}
	return toBizAPI(m), nil
}

func (r *apiRepo) FindByPathMethod(ctx context.Context, path, method string) (*system.Api, error) {
	m, err := query.SysAPI.WithContext(ctx).Where(
		query.SysAPI.Path.Eq(path),
		query.SysAPI.Method.Eq(method),
	).First()
	if err != nil {
		return nil, err
	}
	return toBizAPI(m), nil
}

func (r *apiRepo) List(ctx context.Context, page, pageSize int, filters map[string]string, orderKey string, desc bool) ([]*system.Api, int64, error) {
	q := query.SysAPI.WithContext(ctx)
	if v, ok := filters["path"]; ok && v != "" {
		q = q.Where(query.SysAPI.Path.Like("%" + v + "%"))
	}
	if v, ok := filters["description"]; ok && v != "" {
		q = q.Where(query.SysAPI.Description.Like("%" + v + "%"))
	}
	if v, ok := filters["method"]; ok && v != "" {
		q = q.Where(query.SysAPI.Method.Eq(v))
	}
	if v, ok := filters["api_group"]; ok && v != "" {
		q = q.Where(query.SysAPI.APIGroup.Eq(v))
	}

	total, err := q.Count()
	if err != nil {
		return nil, 0, err
	}

	orderMap := map[string]bool{"id": true, "path": true, "api_group": true, "description": true, "method": true}
	if orderKey != "" && orderMap[orderKey] {
		switch orderKey {
		case "id":
			if desc {
				q = q.Order(query.SysAPI.ID.Desc())
			} else {
				q = q.Order(query.SysAPI.ID)
			}
		case "path":
			if desc {
				q = q.Order(query.SysAPI.Path.Desc())
			} else {
				q = q.Order(query.SysAPI.Path)
			}
		case "api_group":
			if desc {
				q = q.Order(query.SysAPI.APIGroup.Desc())
			} else {
				q = q.Order(query.SysAPI.APIGroup)
			}
		case "description":
			if desc {
				q = q.Order(query.SysAPI.Description.Desc())
			} else {
				q = q.Order(query.SysAPI.Description)
			}
		case "method":
			if desc {
				q = q.Order(query.SysAPI.Method.Desc())
			} else {
				q = q.Order(query.SysAPI.Method)
			}
		}
	} else {
		q = q.Order(query.SysAPI.ID.Desc())
	}

	offset := (page - 1) * pageSize
	list, err := q.Offset(offset).Limit(pageSize).Find()
	if err != nil {
		return nil, 0, err
	}

	apis := make([]*system.Api, len(list))
	for i, m := range list {
		apis[i] = toBizAPI(m)
	}
	return apis, total, nil
}

func (r *apiRepo) FindAll(ctx context.Context) ([]*system.Api, error) {
	list, err := query.SysAPI.WithContext(ctx).Order(query.SysAPI.ID.Desc()).Find()
	if err != nil {
		return nil, err
	}
	apis := make([]*system.Api, len(list))
	for i, m := range list {
		apis[i] = toBizAPI(m)
	}
	return apis, nil
}

func (r *apiRepo) FindByIds(ctx context.Context, ids []uint) ([]*system.Api, error) {
	int64Ids := make([]int64, len(ids))
	for i, id := range ids {
		int64Ids[i] = int64(id)
	}
	list, err := query.SysAPI.WithContext(ctx).Where(query.SysAPI.ID.In(int64Ids...)).Find()
	if err != nil {
		return nil, err
	}
	apis := make([]*system.Api, len(list))
	for i, m := range list {
		apis[i] = toBizAPI(m)
	}
	return apis, nil
}

func (r *apiRepo) GetApiGroups(ctx context.Context) ([]string, map[string]string, error) {
	list, err := query.SysAPI.WithContext(ctx).Find()
	if err != nil {
		return nil, nil, err
	}

	groupMap := make(map[string]bool)
	apiGroupMap := make(map[string]string)
	var groups []string

	for _, m := range list {
		if !groupMap[m.APIGroup] {
			groupMap[m.APIGroup] = true
			groups = append(groups, m.APIGroup)
		}

		if len(m.Path) > 1 {
			parts := strings.Split(m.Path, "/")
			if len(parts) > 1 {
				apiGroupMap[parts[1]] = m.APIGroup
			}
		}
	}
	return groups, apiGroupMap, nil
}

func (r *apiRepo) CreateIgnoreApi(ctx context.Context, path, method string) error {
	m := &model.SysIgnoreAPI{
		Path:   path,
		Method: method,
	}
	return r.db.WithContext(ctx).Create(m).Error
}

func (r *apiRepo) DeleteIgnoreApi(ctx context.Context, path, method string) error {
	return r.db.WithContext(ctx).Unscoped().Where("path = ? AND method = ?", path, method).Delete(&model.SysIgnoreAPI{}).Error
}

func (r *apiRepo) FindAllIgnoreApis(ctx context.Context) ([]*system.IgnoreApi, error) {
	var list []model.SysIgnoreAPI
	if err := r.db.WithContext(ctx).Find(&list).Error; err != nil {
		return nil, err
	}
	result := make([]*system.IgnoreApi, len(list))
	for i, m := range list {
		result[i] = &system.IgnoreApi{
			Path:   m.Path,
			Method: m.Method,
		}
	}
	return result, nil
}

// 转换函数
func toModelAPI(a *system.Api) *model.SysAPI {
	return &model.SysAPI{
		ID:          int64(a.ID),
		Path:        a.Path,
		Description: a.Description,
		APIGroup:    a.ApiGroup,
		Method:      a.Method,
	}
}

func toBizAPI(m *model.SysAPI) *system.Api {
	return &system.Api{
		ID:          uint(m.ID),
		Path:        m.Path,
		Description: m.Description,
		ApiGroup:    m.APIGroup,
		Method:      m.Method,
	}
}
