package orm

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

	"github.com/jinzhu/gorm"

	"gogs.carducci-dante.gov.it/karmen/core/generator"
	"gogs.carducci-dante.gov.it/karmen/core/renderer"
)

type Document struct {
	gorm.Model

	Name string

	OutputPath string
	CloudPath  string

	Jobs []*Job

	RunningJobs   []*Job
	CompletedJobs []*Job

	LastJob *Job `gorm:"-"`
}

type DocumentForUpdate struct {
	Document
}

type DocumentForAdd struct{}

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

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

func GetDocumentAll(args map[string]string) (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 GetDocuments(args map[string]string) (interface{}, error) {
	var documents []*Document
	if err := DB().Order("name").Find(&documents).Error; err != nil {
		return nil, err
	}
	return documents, nil
}

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

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

func UpdateDocument(args map[string]string, r *http.Request) (IDer, error) {
	document, err := GetDocument(args)
	if err != nil {
		return nil, err
	}

	err = renderer.Decode(document, r)
	if err != nil {
		return nil, err
	}

	_, err = SaveDocument(document)
	if err != nil {
		return nil, err
	}
	document, err = GetDocument(args)
	if err != nil {
		return nil, err
	}
	return document.(*Document), nil
}

func AddDocument(args map[string]string, r *http.Request) (IDer, error) {
	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 CreateDocument(document *Document) (*Document, error) {
	if err := DB().Create(document).Error; err != nil {
		return nil, err
	}
	return document, nil
}

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

func GetDocumentForUpdate(args map[string]string) (interface{}, error) {
	var data DocumentForUpdate

	id := args["id"]

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

	return data, nil
}

func GetDocumentForAdd(args map[string]string) (interface{}, error) {
	var data DocumentForAdd

	return data, nil
}

func GetDocumentExecute(args map[string]string) (interface{}, error) {
	var document Document
	if err := DB().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)

	gen := generator.Generators["list"]

	gen.SetJobId(job.ID)

	go gen.Run()

	return &document, nil
}

func (t *Document) RestAPIPath() string {
	return "documents"
}
