package orm

import (
	"fmt"
	"net/http"

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

var selectUniqueSubjectTeachers = `
SELECT DISTINCT teachers.* FROM activities
INNER JOIN teachers on teachers.id=activities.teacher_id
WHERE subject_id=?
`

type Subject struct {
	gorm.Model

	Name  string
	Alias string

	DepartmentID uint `schema:"department_id"`

	Teachers   []*Teacher
	Activities []*Activity
}

type SubjectForUpdate struct {
	Subject Subject

	SelectedDepartment map[uint]string
	AllDepartments     []*Department
}

type SubjectForAdd struct {
	AllDepartments []*Department
}

func (s *Subject) GetID() uint { return s.ID }

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

func GetSubjectAll(args map[string]string) (interface{}, error) {
	var subject Subject

	id := args["id"]

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

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

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

	return &subject, nil
}

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

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

	return subjects, nil
}

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

func UpdateSubject(args map[string]string, r *http.Request) (IDer, error) {
	subject, err := GetSubject(args)
	if err != nil {
		return nil, err
	}
	err = renderer.Decode(subject, r)
	if err != nil {
		return nil, err
	}
	_, err = SaveSubject(subject)
	if err != nil {
		return nil, err
	}
	subject, err = GetSubject(args)
	if err != nil {
		return nil, err
	}
	return subject.(*Subject), nil
}

func AddSubject(args map[string]string, r *http.Request) (IDer, error) {
	subject := new(Subject)
	err := renderer.Decode(subject, r)
	if err != nil {
		return nil, err
	}
	subject, err = CreateSubject(subject)
	if err != nil {
		return nil, err
	}
	return subject, nil
}

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

	if err := DB().Create(subject).Error; err != nil {
		return nil, err
	}
	return subject, nil
}

func DeleteSubject(args map[string]string, r *http.Request) (IDer, error) {
	subject, err := GetSubject(args)
	if err != nil {
		return nil, err
	}
	if err := DB().Unscoped().Delete(subject.(*Subject)).Error; err != nil {
		return nil, err
	}
	return subject.(*Subject), nil
}

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

func GetSubjectForUpdate(args map[string]string) (interface{}, error) {
	var data SubjectForUpdate

	id := args["id"]

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

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

	data.SelectedDepartment = make(map[uint]string)
	data.SelectedDepartment[data.Subject.DepartmentID] = "selected"

	return data, nil
}

func GetSubjectForAdd(args map[string]string) (interface{}, error) {
	var data SubjectForAdd

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

	return data, nil
}
