package coronafana

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"

	"git.paulbsd.com/paulbsd/coronafana/src/config"
	"github.com/cretz/bine/tor"
	"github.com/jinzhu/gorm"
	"github.com/jmoiron/sqlx"
)

// GetData fetch data from open data portal
func GetData(cfg config.Config, d *tor.Dialer) (cr Coronafana, err error) {
	var client = &http.Client{Transport: &http.Transport{DialContext: d.DialContext}}

	log.Println("Getting JSON file ...")

	if cfg.CoronaURL == "" {
		err = fmt.Errorf("CoronaURL is not defined")
		return
	}

	resp, err := client.Get(cfg.CoronaURL)
	if err != nil {
		return
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	err = json.Unmarshal(body, &cr)
	if err != nil {
		return
	}

	log.Println("JSON file fetched.")

	return
}

// InsertGlobalData insert data to MySQL / MariaDB
func (cr Coronafana) InsertGlobalData(cfg config.Config, db sqlx.DB) (err error) {
	var tx *sqlx.Tx
	var i int

	log.Println("Start inserting global data ...")
	tx = db.MustBegin()
	txStmtglobal, err := tx.PrepareNamed(cfg.DbInsertGlobal)

	for _, dt := range cr.GlobalData {
		if t, _ := GetParsedDate(dt.Date); t.After(cr.MaxDateGlobal) {
			_, err = txStmtglobal.Exec(&dt)
			if err != nil {
				return
			}
			i++
		}
	}

	err = tx.Commit()
	if err != nil {
		return
	}

	if i > 0 {
		log.Println(fmt.Sprintf("%d global entries to inserted", i))
	} else {
		log.Println("No entry inserted")
	}

	return
}

// InsertPaysData insert data to MySQL / MariaDB
func (cr Coronafana) InsertPaysData(cfg config.Config, db sqlx.DB) (err error) {
	var tx *sqlx.Tx
	var i int

	log.Println("Start inserting country data ...")
	tx = db.MustBegin()
	cr.ReplacePaysQuotes()

	txStmtpays, err := tx.PrepareNamed(cfg.DbInsertPays)

	for _, dt := range cr.PaysData {
		if t, _ := GetParsedDate(dt.Date); t.After(cr.MaxDatePays) {
			_, err = txStmtpays.Exec(&dt)
			if err != nil {
				return
			}
			i++
		}
	}

	err = tx.Commit()
	if err != nil {
		return
	}

	if i > 0 {
		log.Println(fmt.Sprintf("%d global entries to inserted", i))
	} else {
		log.Println("No entry inserted")
	}
	return
}

// ReplacePaysQuotes escapes quotes in Pays names
func (cr *Coronafana) ReplacePaysQuotes() (err error) {
	for _, pdata := range cr.PaysData {
		pdata.Pays = strings.Replace(pdata.Pays, `'`, `\'`, -1)
	}

	return
}

// GetMaxDates get max dates on table
func (cr *Coronafana) GetMaxDates(cfg config.Config, db sqlx.DB) (err error) {
	var maxDateGlobal, maxDatePays string

	err = db.Get(&maxDateGlobal, cfg.DbMaxDateGlobal)
	if err != nil {
		return
	}

	err = db.Get(&maxDatePays, cfg.DbMaxDatePays)

	if err != nil {
		return
	}

	cr.MaxDateGlobal, err = time.Parse("2006-01-02 15:04:05", maxDateGlobal)
	cr.MaxDatePays, err = time.Parse("2006-01-02 15:04:05", maxDatePays)

	return
}

// GetParsedDate returns parsed date as time.Time
func GetParsedDate(date string) (res time.Time, err error) {
	res, err = time.Parse("2006-01-02T15:04:05", date)
	return
}

// Coronafana is the main struct
type Coronafana struct {
	gorm.Model
	Source        string `json:"Source"`
	Information   string `json:"Information"`
	Utilisation   string `json:"Utilisation"`
	MaxDateGlobal time.Time
	MaxDatePays   time.Time
	GlobalData    []struct {
		Date          string  `json:"Date" gorm:"primary_key" db:"date"`
		Infection     int     `json:"Infection" db:"infection"`
		Deces         int     `json:"Deces" db:"deces"`
		Guerisons     int     `json:"Guerisons" db:"guerisons"`
		TauxDeces     float64 `json:"TauxDeces" db:"tauxdeces"`
		TauxGuerison  float64 `json:"TauxGuerison" db:"tauxguerison"`
		TauxInfection float64 `json:"TauxInfection" db:"tauxinfection"`
	} `json:"GlobalData"`
	PaysData []struct {
		Date          string  `json:"Date" gorm:"primary_key" db:"date"`
		Pays          string  `json:"Pays" db:"pays"`
		Infection     int     `json:"Infection" db:"infection"`
		Deces         int     `json:"Deces" db:"deces"`
		Guerisons     int     `json:"Guerisons" db:"guerisons"`
		TauxDeces     float64 `json:"TauxDeces" db:"tauxdeces"`
		TauxGuerison  float64 `json:"TauxGuerison" db:"tauxguerison"`
		TauxInfection float64 `json:"TauxInfection" db:"tauxinfection"`
	} `json:"PaysData"`
}
