package model

import (
	"github.com/pingcap/errors"
	"gorm.io/gorm"

	"github.com/pingcap-inc/clinic/pkg/datasource/view"
	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/db/model"
	"github.com/pingcap-inc/clinic/pkg/pagination"
)

func GetOrganization(orgID uint, datasource view.DataSourceType) (*db.Organization, error) {
	var (
		org db.Organization
		err error
	)

	err = db.Transaction(func(tx *gorm.DB) error {
		qs := db.NewOrganizationQuerySet(tx)
		return qs.OrgTypeEq(string(datasource)).IDEq(orgID).One(&org)
	})

	return &org, err
}

func ListOrganizations(cond *model.ListOrganizationsCondition, datasource view.DataSourceType) ([]db.Organization, int64, error) {
	var (
		orgs  []db.Organization
		total int64
		err   error
	)
	err = db.Transaction(func(tx *gorm.DB) error {
		// have to use model to query with soft deleted records considered
		q := tx.Model(&db.Organization{})

		q = q.Where("org_type= ?", string(datasource))
		// set base query conditions
		if cond.Name != "" {
			q = q.Where("organizations.name LIKE ?", "%"+cond.Name+"%")
		}
		if len(cond.IDs) > 0 && datasource == view.OPDataSource {
			q = q.Where("organizations.id IN (?)", cond.IDs)
		}
		if len(cond.DbaasOrgIDs) > 0 && datasource == view.CloudDataSource {
			q = q.Where("organizations.dbaas_org_id IN (?)", cond.DbaasOrgIDs)
		}
		if cond.Owner > 0 {
			q = q.Where("organizations.created_by = ?", cond.Owner)
		}
		if cond.StarBy > 0 {
			q = q.Joins("JOIN star ON star.org_id = organizations.id").
				Where("star.user_id = ?", cond.StarBy)
		}

		// query count
		if cond.PerPage > 0 {
			if err = q.Count(&total).Error; err != nil {
				return errors.Annotate(err, "count tenant error")
			}

			startIndex := pagination.GetStartIndex(cond.Page, cond.PerPage)
			q = q.Limit(cond.PerPage).Offset(startIndex)
		}
		// order and find
		if err := q.Order("updated_at DESC").
			Find(&orgs).Error; err != nil {
			return errors.Annotate(err, "fetch tenants failed")
		}
		if cond.PerPage <= 0 {
			// no pagination
			total = int64(len(orgs))
		}
		// extra step to set `isStarByViewer` field if necessary
		if cond.ViewBy > 0 {
			// query star records by the viewer
			orgIDs := make([]uint, 0)
			tx.Table("star").
				Where("user_id = ?", cond.ViewBy).
				Pluck("org_id", &orgIDs)
			lookup := make(map[uint]struct{})
			for _, v := range orgIDs {
				lookup[v] = struct{}{}
			}
			// set org star status by the query result
			for i := range orgs {
				if _, ok := lookup[orgs[i].ID]; ok {
					orgs[i].SetStarredByViewer(true)
				}
			}
		}
		return nil
	})
	if err != nil {
		return nil, 0, err
	}

	return orgs, total, nil
}
