package orm

import (
	"errors"
	"fmt"

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

type Teacher struct {
	gorm.Model
	Credential
	Classes     []Class
	Subjects    []Subject
	Activities  []Activity
	Departments []Department
	Issues      []Issue
	Hours       int
	CurrHours   int
	TheBossId   uint
}

var (
	selectUniqueTeacherClasses = `
SELECT DISTINCT classes.* FROM activities 
INNER JOIN classes on classes.id=activities.class_id 
WHERE teacher_id=?`

	selectUniqueTeacherSubjects = `
SELECT DISTINCT subjects.* FROM activities 
INNER JOIN subjects on subjects.id=activities.subject_id 
WHERE activities.teacher_id=?`
)

func GetTeachers() ([]*Teacher, error) {
	var teachers []*Teacher
	if err := DB().Order("surname,name").Find(&teachers).Error; err != nil {
		return nil, err
	}
	return teachers, nil
}

func (t *Teacher) GetClasses() ([]Class, error) {
	if err := DB().Raw(selectUniqueTeacherClasses, t.ID).Scan(&t.Classes).Error; err != nil {
		return nil, err
	}
	return t.Classes, nil
}

func (t *Teacher) Create(db *gorm.DB, record map[string]interface{}) error {
	result := new(Teacher)

	if name := record["name"]; name == nil {
		return errors.New("Error in creating teacher: field name is empty")
	} else {
		result.Name = name.(string)
	}
	if surname := record["surname"]; surname == nil {
		return errors.New("Error in creating teacher: field surname is empty")
	} else {
		result.Surname = surname.(string)
	}

	db.Create(result)

	return nil
}

func (t *Teacher) CalcHours(tx *gorm.DB) {
	var (
		activities []Activity
		sum        int
	)

	if err := tx.Model(t).Related(&activities).Error; err != nil {
		panic(err)
	}

	for _, a := range activities {
		sum += a.Hours
	}

	if err := tx.Model(t).Update("curr_hours", sum).Error; err != nil {
		panic(err)
	}
}

func (t Teacher) GetSubjectsByClassID(classID uint) []Subject {
	var (
		activities []Activity
		subjects   []Subject
	)

	if err := DB().Model(t).Preload("Subject").Related(&activities).Error; err != nil {
		panic(err)
	}

	for _, a := range activities {
		if a.ClassID == classID {
			subjects = append(subjects, a.Subject)
		}
	}

	return subjects

}

func (t Teacher) IsCoordinator() []Class {
	var (
		classes []Class
		output  []Class
	)

	if err := DB().Model(t).Related(&classes, "Classes").Error; err != nil {
		panic(err)
	}

	for _, class := range classes {
		if class.CoordinatorID == t.ID {
			output = append(output, class)
		}
	}

	return output

}

func (t Teacher) IsCoordinatorInClass(className string) bool {
	var classes []Class

	if err := DB().Model(t).Where("name = ?", className).Related(&classes, "Classes").Error; err != nil {
		panic(err)
	}

	for _, class := range classes {
		if class.CoordinatorID == t.ID {
			return true
		}
	}

	return false

}

func (t Teacher) HasClass(name string) bool {
	var classes []Class

	if err := DB().Model(t).Related(&classes, "Classes").Error; err != nil {
		panic(err)
	}

	for _, class := range classes {
		if class.Name == name {
			return true
		}
	}

	return false

}

func (t Teacher) HasSubject(name string) bool {
	var subjects []Subject

	if err := DB().Model(t).Related(&subjects, "Subjects").Error; err != nil {
		panic(err)
	}

	for _, subject := range subjects {
		if subject.Name == name {
			return true
		}
	}

	return false

}

func (t Teacher) HasSubjectInClass(subjectName string, className string) bool {
	var classes []Class

	if err := DB().Where("name = ?", className).Find(&classes).Error; err != nil {
		panic(err)
	}

	subjects := t.GetSubjectsByClassID(classes[0].ID)

	for _, s := range subjects {
		if s.Name == subjectName {
			return true
		}
	}

	return false
}

func (t Teacher) IsMinuter() []Class {
	var (
		classes []Class
		output  []Class
	)

	if err := DB().Model(t).Related(&classes, "Classes").Error; err != nil {
		panic(err)
	}

	for _, class := range classes {
		if class.MinuterID == t.ID {
			output = append(output, class)
		}
	}

	return output

}

// func (t *Teacher) CheckForIssues(tx *gorm.DB) {
// 	if t.CurrHours != t.Hours {
// 		var issues []Issue
// 		if err := tx.Where("type = ? and reported = ?", IssueTeacherHours, 1).Model(t).Related(&issues).Error; err != nil {
// 			panic(err)
// 		}
// 		if len(issues) == 0 {
// 			desc := fmt.Sprintf(
// 				"Le ore relative alla cattedra del docente %s %s non coincidono con la somma delle ore delle sue attività (%d/%d)",
// 				t.Surname, t.Name,
// 				t.CurrHours, t.Hours)
// 			issue := Issue{
// 				Type:        IssueTeacherHours,
// 				Description: desc,
// 				Reported:    true,
// 				TeacherID:   t.ID,
// 			}
// 			if err := tx.Save(&issue).Error; err != nil {
// 				panic(err)
// 			}
// 		} else {
// 			issues[0].Description = fmt.Sprintf(
// 				"Le ore relative alla cattedra del docente %s %s non coincidono con la somma delle ore delle sue attività (%d/%d)",
// 				t.Surname, t.Name,
// 				t.CurrHours, t.Hours)
// 			if err := tx.Save(&issues[0]).Error; err != nil {
// 				panic(err)
// 			}
// 		}
// 	} else {
// 		var issues []Issue
// 		if err := tx.Where("type = ? and reported = ?", IssueTeacherHours, 1).Model(t).Related(&issues).Error; err != nil {
// 			panic(err)
// 		}
// 		if len(issues) > 0 {
// 			if err := tx.Delete(&issues[0]).Error; err != nil {
// 				panic(err)
// 			}
// 		}

// 	}
// }

func (t *Teacher) generateCredential() error {
	t.Username = t.GenerateUsername()
	t.Password = t.GenerateSaltedPassword(t.Username)
	t.Email = fmt.Sprintf("%s@%s", t.Username, config.Config.Domain)
	return nil
}

func (t *Teacher) CheckForIssues(tx *gorm.DB) {
	// issue := newTeacherMaxHoursIssue(t)
	// if t.CurrHours != t.Hours {
	// 	issue.createIfNotExist(t)
	// } else {
	// 	issue.resetIfExist(t)
	// }
}

func (t *Teacher) BeforeCreate(tx *gorm.DB) error {
	// FIXME: should re-generate only if related columns are modified
	t.generateCredential()
	return nil
}

func (t *Teacher) BeforeUpdate(tx *gorm.DB) error {
	// FIXME: should re-generate only if related columns are modified
	t.generateCredential()
	return nil
}

// func (t *Teacher) AfterUpdate(tx *gorm.DB) error {
// 	// FIXME: should re-generate only if related columns are modified
// 	t.generateCredential()
// 	// t.CheckForIssues(tx)
// 	return nil
// }

func (t *Teacher) AfterDelete(tx *gorm.DB) error {
	var issues []Issue
	if err := tx.Where("type = ?", IssueTeacherHours).Model(t).Related(&issues).Error; err != nil {
		panic(err)
	}
	if err := tx.Delete(&issues).Error; err != nil {
		panic(err)
	}
	return nil
}
