package models

import (
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"time"

	"forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common"
	"forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/constants"
	"gorm.io/gorm"
)

type GrdfConsent struct {
	Base
	Firstname  string    `json:"firstname"`
	Lastname   string    `json:"lastname"`
	Pce        string    `json:"pce"`
	PostalCode string    `json:"postalCode"`
	EndDate    time.Time `json:"endDate"`
}

// This type is only used for Swagger documentation
type GrdfConsentSwagger struct {
	ID         int       `json:"ID"`
	CreatedAt  time.Time `json:"CreatedAt"`
	UpdatedAt  time.Time `json:"UpdatedAt"`
	DeletedAt  time.Time `json:"DeletedAt"`
	Firstname  string    `json:"firstname"`
	Lastname   string    `json:"lastname"`
	Pce        string    `json:"pce"`
	PostalCode string    `json:"postalCode"`
	EndDate    time.Time `json:"endDate"`
}

// GetConsentById godoc
//
//	@Summary		Get details of a specific consent
//	@Description	Get details of a specific consent
//	@Tags			consent
//	@Produce		json
//	@Success		200	{object}	GrdfConsentSwagger
//	@Failure		404	{string}	string	"Not found"
//	@Param			id	path		int		true	"ID of the consent"
//	@Router			/api/grdf/consent/{id} [get]
func (dh *DataHandler) GetGrdfConsentById(w http.ResponseWriter, r *http.Request) {
	id, err := common.IdFromRequest(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var consent GrdfConsent
	err = dh.sqlClient.First(&consent, "id = ?", id).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			http.Error(w, "consent not found", http.StatusNotFound)
			return
		}
		http.Error(w, "error while finding consent", http.StatusInternalServerError)
		return
	}

	w.Header().Set(constants.ContentType, constants.Json)
	json.NewEncoder(w).Encode(consent)

	log.Printf("| get consent | name : %v | %v", consent.Lastname, r.RemoteAddr)
}

// PostConsent godoc
//
//	@Summary		Create a new consent
//	@Description	Create a new consent
//	@Tags			consent
//	@Produce		json
//	@Success		201	{object}	GrdfConsentSwagger
//	@Failure		400	{string}	string	"Bad request"
//	@Failure		500	{string}	string	"couldn't create consent"
//	@Param			id	path		int		true	"ID of the consent"
//	@Router			/api/grdf/consent [post]
func (dh *DataHandler) PostGrdfConsent(w http.ResponseWriter, r *http.Request) {
	if r.Body == http.NoBody {
		http.Error(w, "request body is empty", http.StatusBadRequest)
		return
	}

	decoder := json.NewDecoder(r.Body)
	var consent GrdfConsent
	err := decoder.Decode(&consent)
	if err != nil {
		http.Error(w, "couldn't parse body", http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}

	// Create a consent in SQL
	err = dh.sqlClient.Create(&consent).Error
	if err != nil {
		http.Error(w, "couldn't create consent", http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}

	w.Header().Set(constants.ContentType, constants.Json)
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(consent)

	log.Printf("| new consent | name : %v | %v", consent.Lastname, r.RemoteAddr)
}

// DeleteConsentById godoc
//
//	@Summary		Delete a specific consent
//	@Description	Delete a specific consent
//	@Tags			consent
//	@Produce		json
//	@Success		200	{object}	GrdfConsentSwagger
//	@Failure		404	{string}	string	"Not found"
//	@Failure		500	{string}	string	"Not found"
//	@Param			id	path		int		true	"ID of the consent"
//	@Router			/api/grdf/consent/{id} [delete]
func (dh *DataHandler) DeleteGrdfConsentById(w http.ResponseWriter, r *http.Request) {
	id, err := common.IdFromRequest(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var consent = GrdfConsent{}
	err = dh.sqlClient.First(&consent, "id = ?", id).Error
	if err != nil {
		http.Error(w, "couldn't find consent", http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}

	// Update and save consent in MySQL
	consent.EndDate = time.Now()
	err = dh.sqlClient.Save(&consent).Error
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Println(err.Error())
		return
	}

	dh.sqlClient.Delete(&consent)

	log.Printf("| deleted consent | id : %v | %v", id, r.RemoteAddr)
}

// SearchConsent godoc
//
//	@Summary		Search for consents
//	@Description	Search for consents based on the pointID
//	@Tags			consent
//	@Produce		json
//	@Success		200		{array}		GrdfConsentSwagger
//	@Failure		400		{string}	string	"Not found"
//	@Param			search	query		string	true	"pointID to search"
//	@Router			/api/admin/consent [get]
func (dh *DataHandler) SearchGrdfConsent(w http.ResponseWriter, r *http.Request) {
	search := r.URL.Query().Get("search")

	page, limit, err := common.PageLimitFromRequest(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var totalRows int64
	dh.sqlClient.Unscoped().Model(GrdfConsent{}).Where("pce LIKE ?", "%"+search+"%").Count(&totalRows)
	offset := page * limit

	var consents []GrdfConsent
	dh.sqlClient.Unscoped().Order("created_at desc").Offset(offset).Limit(limit).Where("pce LIKE ?", "%"+search+"%").Find(&consents)

	var pagination struct {
		TotalRows int64         `json:"totalRows"`
		Rows      []GrdfConsent `json:"rows"`
	}
	pagination.TotalRows = totalRows
	pagination.Rows = consents

	w.Header().Set(constants.ContentType, constants.Json)
	json.NewEncoder(w).Encode(pagination)

	log.Printf("| get all consents | limit : %d | page : %d | %v", limit, page, r.RemoteAddr)

}