package orm

import (
	"fmt"
	"net/http"

	"github.com/jinzhu/gorm"
	"gogs.carducci-dante.gov.it/karmen/core/renderer"
)

var (
	selectClassActivities = "SELECT * FROM activities WHERE class_id=?"

	selectUniqueClassTeachers = `
SELECT teachers.* FROM activities
INNER JOIN teachers on teachers.id=activities.teacher_id
WHERE class_id=?
`
)

type Class struct {
	gorm.Model

	Name    string
	Alias   string
	Year    int
	Section string
	Field   string

	CoordinatorID uint `schema:"coordinator_id"`
	MinuterID     uint `schema:"minuter_id"`

	Coordinator Teacher
	Minuter     Teacher

	Teachers   []*Teacher
	Activities []*Activity
	Students   []*Student
}

type ClassForUpdate struct {
	Class               Class
	AllTeachers         []*Teacher
	SelectedCoordinator map[uint]string
	SelectedMinuter     map[uint]string
}

type ClassForAdd struct {
	AllTeachers []*Teacher
}

func (t *Class) GetID() uint { return t.ID }

func GetClass(args map[string]string) (interface{}, error) {
	var class Class
	if err := DB().First(&class, args["id"]).Error; err != nil {
		return nil, err
	}
	return &class, nil
}

func GetClassAll(args map[string]string) (interface{}, error) {
	var class Class

	id := args["id"]

	if err := DB().First(&class, id).Error; err != nil {
		return nil, err
	}

	if err := DB().Raw(selectUniqueClassTeachers, id).Scan(&class.Teachers).Error; err != nil {
		return nil, err
	}

	if err := DB().Preload("Teacher").Preload("Subject").Preload("Class").Where("class_id=?", id).Find(&class.Activities).Error; err != nil {
		return nil, err
	}

	if class.CoordinatorID != 0 {
		if !DB().First(&class.Coordinator, class.CoordinatorID).RecordNotFound() {
			if err := DB().First(&class.Coordinator, class.CoordinatorID).Error; err != nil {
				return nil, err
			}
		}
	}

	if class.MinuterID != 0 {
		if !DB().First(&class.Minuter, class.MinuterID).RecordNotFound() {
			if err := DB().First(&class.Minuter, class.MinuterID).Error; err != nil {
				return nil, err
			}
		}
	}

	return &class, nil
}

func GetClasses(args map[string]string) (interface{}, error) {
	var classes []*Class
	if err := DB().Order("name").Find(&classes).Error; err != nil {
		return nil, err
	}
	return classes, nil
}

func GetClassesAll(args map[string]string) (interface{}, error) {
	var classes []*Class
	if err := DB().Order("name").Find(&classes).Error; err != nil {
		return nil, err
	}
	for _, class := range classes {
		class.GetTeachers()
	}

	for _, class := range classes {
		class.GetActivities()
	}

	for _, class := range classes {
		if class.CoordinatorID != 0 {
			if !DB().First(&class.Coordinator, class.CoordinatorID).RecordNotFound() {
				if err := DB().First(&class.Coordinator, class.CoordinatorID).Error; err != nil {
					return nil, err
				}
			}
		}

		if class.MinuterID != 0 {
			if !DB().First(&class.Minuter, class.MinuterID).RecordNotFound() {
				if err := DB().First(&class.Minuter, class.MinuterID).Error; err != nil {
					return nil, err
				}
			}
		}

	}

	return classes, nil
}

func SaveClass(class interface{}) (interface{}, error) {
	if err := DB().Save(class).Error; err != nil {
		return nil, err
	}
	return class, nil
}

func UpdateClass(args map[string]string, r *http.Request) (IDer, error) {
	class, err := GetClass(args)
	if err != nil {
		return nil, err
	}
	err = renderer.Decode(class, r)
	if err != nil {
		return nil, err
	}
	_, err = SaveClass(class)
	if err != nil {
		return nil, err
	}
	class, err = GetClass(args)
	if err != nil {
		return nil, err
	}
	return class.(*Class), nil
}

func AddClass(args map[string]string, r *http.Request) (IDer, error) {
	class := new(Class)
	err := renderer.Decode(class, r)
	if err != nil {
		return nil, err
	}
	class, err = CreateClass(class)
	if err != nil {
		return nil, err
	}
	return class, nil
}

func CreateClass(class *Class) (*Class, error) {
	var classes []*Class
	if err := DB().Where("name=?", class.Name).Find(&classes).Error; err != nil {
		return nil, err
	}
	if len(classes) > 0 {
		return nil, fmt.Errorf("Class %s already exists!", class.Name)
	}
	if err := DB().Create(class).Error; err != nil {
		return nil, err
	}
	return class, nil
}

func DeleteClass(args map[string]string, r *http.Request) (IDer, error) {
	class, err := GetClass(args)
	if err != nil {
		return nil, err
	}
	if err := DB().Unscoped().Delete(class.(*Class)).Error; err != nil {
		return nil, err
	}
	return class.(*Class), nil
}

func (c *Class) GetTeachers() ([]*Teacher, error) {
	if err := DB().Raw(selectUniqueClassTeachers, c.ID).Scan(&c.Teachers).Error; err != nil {
		return nil, err
	}
	return c.Teachers, nil
}

func (c *Class) GetActivities() ([]*Activity, error) {
	if err := DB().Preload("Teacher").Preload("Subject").Preload("Class").Where("class_id=?", c.ID).Find(&c.Activities).Error; err != nil {
		return nil, err
	}
	return c.Activities, nil
}

func GetClassForUpdate(args map[string]string) (interface{}, error) {
	var data ClassForUpdate

	id := args["id"]

	if err := DB().Preload("Coordinator").Preload("Minuter").First(&data.Class, id).Error; err != nil {
		return nil, err
	}

	if err := DB().Find(&data.AllTeachers).Error; err != nil {
		return nil, err
	}

	data.SelectedMinuter = make(map[uint]string)
	data.SelectedMinuter[data.Class.MinuterID] = "selected"

	data.SelectedCoordinator = make(map[uint]string)
	data.SelectedCoordinator[data.Class.CoordinatorID] = "selected"

	return data, nil
}

func GetClassForAdd(args map[string]string) (interface{}, error) {
	var data ClassForAdd

	if err := DB().Find(&data.AllTeachers).Error; err != nil {
		return nil, err
	}

	return data, nil
}
