package orm

import (
	"fmt"
	"net/http"

	"github.com/jinzhu/gorm"
	"gogs.carduccidante.edu.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"`
	Department   *Department

	Teachers   []*Teacher
	Activities []*Activity

	AllDepartments []*Department   `gorm:"-"`
	Selected       map[uint]string `gorm:"-"`
}

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

func (s *Subject) Create(args map[string]string, r *http.Request) (interface{}, error) {
	if r.Method == "GET" {
		subject := new(Subject)
		if err := DB().Find(&subject.AllDepartments).Error; err != nil {
			return nil, err
		}
		return subject, nil
	} else {
		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 (s *Subject) Read(args map[string]string, r *http.Request) (interface{}, error) {
	var subject Subject

	id := args["id"]

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

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

	return &subject, nil
}

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

func (s *Subject) Update(args map[string]string, r *http.Request) (interface{}, error) {
	if r.Method == "GET" {
		result, err := s.Read(args, r)
		if err != nil {
			return nil, err
		}

		subject := result.(*Subject)

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

		subject.Selected = make(map[uint]string)
		subject.Selected[subject.DepartmentID] = "selected"

		return subject, nil
	} else {
		subject, err := s.Read(args, r)
		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 = s.Read(args, r)
		if err != nil {
			return nil, err
		}

		return subject.(*Subject), nil
	}
}

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

func SaveSubject(subject interface{}) (interface{}, error) {
	if err := DB().Omit("Department").Save(subject).Error; 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 (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
}
