package orm

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/jinzhu/gorm"

	"gogs.carduccidante.edu.it/karmen/core/generator"
	"gogs.carduccidante.edu.it/karmen/core/renderer"
)

type GeneratorType struct {
	gorm.Model

	Name string
}

func (g *GeneratorType) String() string { return g.Name }

type Document struct {
	gorm.Model

	Name string

	OutputPath string
	CloudPath  string

	GeneratorTypeID uint `schema:"generator_type_id"`
	GeneratorType   *GeneratorType

	KeepArtifacts bool

	Jobs []*Job

	RunningJobs   []*Job
	CompletedJobs []*Job

	LastJob *Job `gorm:"-"`

	AllGeneratorTypes     []*GeneratorType
	SelectedGeneratorType map[uint]string `gorm:"-"`
}

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

func (d *Document) Create(args map[string]string, r *http.Request) (interface{}, error) {
	if r.Method == "GET" {
		document := new(Document)
		if err := DB().Find(&document.AllGeneratorTypes).Error; err != nil {
			return nil, err
		}
		return document, nil
	} else {
		document := new(Document)
		err := renderer.Decode(document, r)
		if err != nil {
			return nil, err
		}
		document, err = CreateDocument(document)
		if err != nil {
			return nil, err
		}

		return document, nil
	}
}

func (d *Document) Read(args map[string]string, r *http.Request) (interface{}, error) {
	var document Document

	id := args["id"]

	if err := DB().Preload("RunningJobs", "end = ?", time.Time{}).Where("id = ?", id).Find(&document).Error; err != nil {
		return nil, err
	}

	if err := DB().Preload("CompletedJobs", func(db *gorm.DB) *gorm.DB {
		return db.Where("end <> ?", time.Time{}).Limit(5).Order("end DESC")
	}).Where("id = ?", id).Find(&document).Error; err != nil {
		return nil, err
	}

	return &document, nil
}

func (d *Document) ReadAll(args map[string]string, r *http.Request) (interface{}, error) {
	var documents []*Document

	if err := DB().Preload("GeneratorType").Order("name").Find(&documents).Error; err != nil {
		return nil, err
	}
	return documents, nil
}

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

		document := result.(*Document)

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

		document.SelectedGeneratorType = make(map[uint]string)
		document.SelectedGeneratorType[document.GeneratorTypeID] = "selected"

		return document, nil
	} else {
		document, err := d.Read(args, r)
		if err != nil {
			return nil, err
		}

		// FIXME: Should not be hard set.
		document.(*Document).KeepArtifacts = false

		err = renderer.Decode(document, r)
		if err != nil {
			return nil, err
		}
		_, err = SaveDocument(document)
		if err != nil {
			return nil, err
		}
		document, err = d.Read(args, r)
		if err != nil {
			return nil, err
		}

		return document.(*Document), nil
	}
}

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

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

func CreateDocument(document *Document) (*Document, error) {
	if err := DB().Create(document).Error; err != nil {
		return nil, err
	}
	return document, nil
}

func GetDocumentExecute(args map[string]string, r *http.Request) (interface{}, error) {
	var document Document
	if err := DB().Preload("GeneratorType").First(&document, args["id"]).Error; err != nil {
		return nil, err
	}

	// Create a new job
	job := new(Job)
	job.DocumentID = document.ID
	err := DB().Create(job).Error
	if err != nil {
		return nil, err
	}

	document.LastJob = job

	log.Printf("Start concurrent job %d for generator %s", job.ID, document.Name)

	if document.GeneratorType != nil {
		gen, ok := generator.Generators[document.GeneratorType.Name]
		if !ok {
			return nil, fmt.Errorf("Generator type %s not found!", document.GeneratorType.Name)
		}
		gen.SetJobId(job.ID)
		go gen.Run()
	} else {
		return nil, errors.New("Generator type not defined!")
	}

	return &document, nil
}
