package models

import (
	"errors"
	"strings"
	"time"

	"github.com/lib/pq"
)

type Contract struct {
	BaseModel
	Name                string         `json:"name"`
	DeviceIDs           pq.Int64Array  `json:"deviceIds" gorm:"type:integer[]"`
	BuyerID             uint           `json:"buyerId"`
	SellerID            uint           `json:"sellerId"`
	Description         string         `json:"description"`
	StartPlaceName      string         `json:"startPlaceName"`
	StartLat            float64        `json:"startLat"`
	StartLon            float64        `json:"startLon"`
	EndPlaceName        string         `json:"endPlaceName"`
	EndLat              float64        `json:"endLat"`
	EndLon              float64        `json:"endLon"`
	StartTime           time.Time      `json:"startTime"`
	EndTime             time.Time      `json:"endTime"`
	Status              string         `json:"status"`
	BlockchainSecret    string         `json:"blockchainSecret"`
	ContractInfos       []ContractInfo `json:"contractInfos"`
	ProductID           uint           `json:"productId"`
	TemplateID          uint           `json:"templateId"`
	MaxTemp             float64        `json:"maxTemp"`
	MinTemp             float64        `json:"minTemp"`
	ArrivalDate         time.Time      `json:"arrivalDate"`
	PenaltyType         string         `json:"penaltyType"`
	PenaltyValue        int            `json:"penaltyValue"`
	PenaltyRec          string         `json:"penaltyRec"`
	BuyerName           string         `json:"buyerName" gorm:"-"`
	SellerName          string         `json:"sellerName" gorm:"-"`
	ProductName         string         `json:"productName" gorm:"-"`
	NumberOfDevices     int            `json:"numberOfDevices" gorm:"-"`
	DevicesImeis        []string       `json:"devicesImeis" gorm:"-"`
	DevicesImeisScanner PQStringArray  `json:"contractDevicesImeis" gorm:"-"`
}

type PQStringArray []string

func (a *PQStringArray) Scan(src interface{}) error {
	if src == nil {
		*a = nil
		return nil
	}

	// The database driver will give us a string in the format "{item1,item2,item3}"
	// We need to convert this to a slice
	var str string
	switch src := src.(type) {
	case []byte:
		str = string(src)
	case string:
		str = src
	default:
		return errors.New("incompatible type for PQStringArray")
	}

	// Remove the curly braces and split the string
	str = strings.Trim(str, "{}")
	if str != "" {
		*a = strings.Split(str, ",")
	} else {
		*a = []string{}
	}
	return nil
}

type DashboardContractResponse struct {
	BaseModel
	Name            string         `json:"name"`
	DeviceIDs       pq.Int64Array  `json:"deviceIds" gorm:"type:integer[]"`
	BuyerID         uint           `json:"buyerId"`
	SellerID        uint           `json:"sellerId"`
	Description     string         `json:"description"`
	StartLat        float64        `json:"startLat"`
	StartLon        float64        `json:"startLon"`
	EndLat          float64        `json:"endLat"`
	EndLon          float64        `json:"endLon"`
	StartTime       time.Time      `json:"startTime"`
	EndTime         time.Time      `json:"endTime"`
	Status          string         `json:"status"`
	BlockchainID    string         `json:"blockchainId"`
	ContractInfos   []ContractInfo `json:"contractInfos"`
	ProductID       uint           `json:"productId"`
	MaxTemp         float64        `json:"maxTemp"`
	MinTemp         float64        `json:"minTemp"`
	ArrivalDate     time.Time      `json:"arrivalDate"`
	PenaltyType     string         `json:"penaltyType"`
	PenaltyValue    int            `json:"penaltyValue"`
	PenaltyRec      string         `json:"penaltyRec"`
	BuyerName       string         `json:"buyerName" gorm:"-"`
	NumberOfDevices int            `json:"numberOfDevices" gorm:"-"`
}

type ContractResponse struct {
	BaseModel
	Name      string        `json:"name"`
	DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"`
	Seller    struct {
		ID   uint   `json:"id"`
		Name string `json:"name"`
	} `json:"seller"`
	Buyer struct {
		ID   uint   `json:"id"`
		Name string `json:"name"`
	} `json:"buyer"`
	Start struct {
		Name string    `json:"name"`
		Lat  float64   `json:"lat"`
		Lon  float64   `json:"lon"`
		Time time.Time `json:"time"`
	} `json:"start"`
	End struct {
		Name string    `json:"name"`
		Lat  float64   `json:"lat"`
		Lon  float64   `json:"lon"`
		Time time.Time `json:"time"`
	} `json:"end"`
	Product struct {
		ID   uint   `json:"id"`
		Name string `json:"name"`
	} `json:"product"`
	Description   string         `json:"description"`
	Status        string         `json:"status"`
	BlockchainID  string         `json:"blockchainId"`
	ContractInfos []ContractInfo `json:"contractInfos"`
	MaxTemp       float64        `json:"maxTemp"`
	MinTemp       float64        `json:"minTemp"`
	ArrivalDate   time.Time      `json:"arrivalDate"`
	PenaltyType   string         `json:"penaltyType"`
	PenaltyValue  int            `json:"penaltyValue"`
	PenaltyRec    string         `json:"penaltyRec"`
	DeviceImeis   []string       `json:"devicesImeis"`
}

func ConvertContractsToContractResponse(contracts []Contract) []ContractResponse {
	contractResponses := []ContractResponse{}
	for _, contract := range contracts {
		contractResponse := ConvertContractToContractResponse(contract)
		contractResponses = append(contractResponses, contractResponse)
	}
	return contractResponses
}

func ConvertContractToContractResponse(contract Contract) ContractResponse {
	contractResponse := ContractResponse{
		BaseModel: BaseModel{ID: contract.ID, CreatedAt: contract.CreatedAt, UpdatedAt: contract.UpdatedAt},
		Name:      contract.Name,
		DeviceIDs: contract.DeviceIDs,
		Seller: struct {
			ID   uint   "json:\"id\""
			Name string "json:\"name\""
		}{
			ID:   contract.SellerID,
			Name: contract.SellerName,
		},
		Buyer: struct {
			ID   uint   "json:\"id\""
			Name string "json:\"name\""
		}{
			ID:   contract.BuyerID,
			Name: contract.BuyerName,
		},
		Start: struct {
			Name string    "json:\"name\""
			Lat  float64   "json:\"lat\""
			Lon  float64   "json:\"lon\""
			Time time.Time "json:\"time\""
		}{
			Name: contract.StartPlaceName,
			Lat:  contract.StartLat,
			Lon:  contract.StartLon,
			Time: contract.StartTime,
		},
		End: struct {
			Name string    "json:\"name\""
			Lat  float64   "json:\"lat\""
			Lon  float64   "json:\"lon\""
			Time time.Time "json:\"time\""
		}{
			Name: contract.EndPlaceName,
			Lat:  contract.EndLat,
			Lon:  contract.EndLon,
			Time: contract.EndTime,
		},
		Product: struct {
			ID   uint   "json:\"id\""
			Name string "json:\"name\""
		}{
			ID:   contract.ProductID,
			Name: contract.ProductName,
		},
		Description:   contract.Description,
		Status:        contract.Status,
		BlockchainID:  contract.BlockchainSecret,
		ContractInfos: contract.ContractInfos,
		MaxTemp:       contract.MaxTemp,
		MinTemp:       contract.MinTemp,
		ArrivalDate:   contract.ArrivalDate,
		PenaltyType:   contract.PenaltyType,
		PenaltyValue:  contract.PenaltyValue,
		PenaltyRec:    contract.PenaltyRec,
		DeviceImeis:   contract.DevicesImeis,
	}

	return contractResponse
}

func ConvertContractToDashboardResponse(contracts []Contract) []DashboardContractResponse {
	contractResponses := []DashboardContractResponse{}
	for _, contract := range contracts {
		contractResponse := DashboardContractResponse{
			BaseModel: BaseModel{
				ID:        contract.ID,
				CreatedAt: contract.CreatedAt,
				UpdatedAt: contract.UpdatedAt,
			},
			Name:            contract.Name,
			DeviceIDs:       contract.DeviceIDs,
			BuyerID:         contract.BuyerID,
			SellerID:        contract.SellerID,
			Description:     contract.Description,
			StartLat:        contract.StartLat,
			StartLon:        contract.StartLon,
			EndLat:          contract.EndLat,
			EndLon:          contract.EndLon,
			StartTime:       contract.StartTime,
			EndTime:         contract.EndTime,
			Status:          contract.Status,
			BlockchainID:    contract.BlockchainSecret,
			ContractInfos:   contract.ContractInfos,
			ProductID:       contract.ProductID,
			MaxTemp:         contract.MaxTemp,
			MinTemp:         contract.MinTemp,
			ArrivalDate:     contract.ArrivalDate,
			PenaltyType:     contract.PenaltyType,
			PenaltyValue:    contract.PenaltyValue,
			PenaltyRec:      contract.PenaltyRec,
			BuyerName:       contract.BuyerName,
			NumberOfDevices: contract.NumberOfDevices,
		}
		contractResponses = append(contractResponses, contractResponse)
	}
	return contractResponses
}

func ConvertContractToListResponse(contracts []Contract) []ListContractResponse {
	listInvoiceResponses := []ListContractResponse{}

	// Get all statuses
	statuses := GetContractStatuses()
	statusMap := make(map[string]Status)
	for _, s := range statuses {
		statusMap[s.Key] = s
	}

	for _, contract := range contracts {
		// Get the status based on Value in the DB
		status, ok := statusMap[contract.Status]
		if !ok {
			status = Status{Key: "", Value: ""}
		}

		listInvoiceResponse := ListContractResponse{
			Status:          KeyValue{Key: status.Key, Value: status.Value},
			Buyer:           CompanyShortResponse{ID: int(contract.BuyerID), Name: contract.BuyerName},
			ContractID:      int(contract.ID),
			ContractName:    contract.Name,
			DateCreated:     contract.CreatedAt,
			NumberOfDevices: contract.NumberOfDevices,
			DeviceImeis:     contract.DevicesImeis,
		}
		listInvoiceResponses = append(listInvoiceResponses, listInvoiceResponse)
	}
	return listInvoiceResponses

}

const ContractStatusActive = "active"
const ContractStatusPending = "pending"
const ContractStatusDraft = "draft"
const ContractStatusSigned = "signed"
const ContractStatusReadyForActivation = "ready_for_activation"
const ContractStatusExecuted = "executed"
const ContractStatusRevoked = "revoked"

const PenaltyTypeAmount = "amount"
const PenaltyTypePercentage = "percentage"

const PenaltyRecDaily = "daily"
const PenaltyRecMonthly = "monthly"

type Status struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

type ActiveContractsResponse struct {
	ActiveCount      int64                       `json:"active"`
	ExecutedCount    int64                       `json:"executed"`
	MonthlyContracts map[string]map[string]int64 `json:"monthly"`
}

func GetContractStatuses() []Status {
	return []Status{
		{Value: "Active", Key: "active"},
		{Value: "Pending signature", Key: "pending"},
		{Value: "Draft", Key: "draft"},
		{Value: "Signed", Key: "signed"},
		{Value: "Ready for Activation", Key: "ready_for_activation"},
		{Value: "Executed", Key: "executed"},
		{Value: "Revoked", Key: "revoked"},
	}
}

type ListContractResponse struct {
	Status          KeyValue             `json:"status"`
	Buyer           CompanyShortResponse `json:"buyer"`
	ContractID      int                  `json:"contractID"`
	ContractName    string               `json:"contractName"`
	NumberOfDevices int                  `json:"numberOfDevices"`
	DateCreated     time.Time            `json:"dateCreated"`
	DeviceImeis     []string             `json:"deviceImeis"`
}

type CreateContractRequestPayload struct {
	SellerID       uint    `json:"sellerId" binding:"required"`
	BuyerID        uint    `json:"buyerId" binding:"required"`
	Description    string  `json:"description"`
	ProductID      uint    `json:"productId"`
	MinTemp        float64 `json:"minTemp" binding:"required"`
	MaxTemp        float64 `json:"maxTemp" binding:"required"`
	ArrivalDate    int64   `json:"arrivalDate"`
	PenaltyType    string  `json:"penaltyType"`
	PenaltyValue   int     `json:"penaltyValue"`
	PenaltyRec     string  `json:"penaltyRec"`
	StartPlaceName string  `json:"startPlaceName"`
	StartLat       float64 `json:"startLat"`
	StartLon       float64 `json:"startLon"`
	EndPlaceName   string  `json:"endPlaceName"`
	EndLat         float64 `json:"endLat"`
	EndLon         float64 `json:"endLon"`
	Name           string  `json:"name"`
}

func (Contract) Update() (bool, error) {
	return false, nil
}
func (Contract) Create() (bool, error) {
	return false, nil
}
func (Contract) Delete() (bool, error) {
	return false, nil
}
