package entities

import (
	"database/sql/driver"
	"encoding/json"
	"time"

	"github.com/UpmeshLTD/urus-aio/infrastructure/common"
	"gorm.io/datatypes"
)

type Shipment struct {
	BaseUuidEntity

	FulfillmentID        string
	CourierTrackingID    *string
	Status               ShipmentStatus
	StateDate            time.Time
	CancelReasonText     string
	CancelReasonCode     string
	CourierStatus        string
	CourierWebhookStatus string
	CourierID            int64
	DeliveryPlanID       int64
	WeightDimension      ShipmentWeightDimension
	Fee                  float64

	PickupPointID     int64
	PickupTime        *time.Time
	PickupStartTime   *time.Time
	PickupEndTime     *time.Time
	PickupInstruction string

	CartID          string
	ShippingGroupID *string
	ReceiverInfo    ShipmentReceiverInfo
	Items           ShipmentItems

	Courier             Courier
	PickupPoint         PickupPoint
	CourierDeliveryPlan CourierDeliveryPlan `gorm:"foreignKey:DeliveryPlanID"`

	WorkspaceID      string
	BuyerID          string
	IsLabelGenerated bool
	LabelURL         string

	CourierMetadata datatypes.JSONMap
	LabelMetadata   datatypes.JSONMap
	Code            *string

	PaidOrderID string

	ArchivedAt *time.Time
	CheckedAt  *time.Time
}

/*--------------------------------------------------------------------------------------------------------------------*/

type ShipmentWeightDimension struct {
	Weight float64
	Width  float64
	Length float64
	Height float64
}

func (v ShipmentWeightDimension) Value() (driver.Value, error) {
	return json.Marshal(&v)
}

func (v *ShipmentWeightDimension) Scan(value interface{}) error {
	return jsonScan(value, v, func() {
		*v = ShipmentWeightDimension{}
	})
}

/*--------------------------------------------------------------------------------------------------------------------*/

type SubLevelLocation struct {
	InternationalName string
	LocalizedName     string
}

type ShipmentReceiverInfo struct {
	Name              string
	RecipientName     string
	Email             string
	ContactNumber     string
	CountryCode       string
	PostalCode        string
	AddressLine1      string
	AddressLine2      string
	UnitNumber        string
	SubLevelLocations []SubLevelLocation
	Instructions      string
}

func (v ShipmentReceiverInfo) Value() (driver.Value, error) {
	return json.Marshal(&v)
}

func (v *ShipmentReceiverInfo) Scan(value interface{}) error {
	return jsonScan(value, v, func() {
		*v = ShipmentReceiverInfo{}
	})
}

func (v ShipmentReceiverInfo) Address() *Address {
	var subLevelLocations []Location
	for _, s := range v.SubLevelLocations {
		subLevelLocations = append(subLevelLocations, Location{
			LocalizedName:     s.LocalizedName,
			InternationalName: s.InternationalName,
		})
	}
	return &Address{
		Address1:          v.AddressLine1,
		Address2:          v.AddressLine2,
		SubLevelLocations: subLevelLocations,
		PostalCode:        v.PostalCode,
	}
}

/*--------------------------------------------------------------------------------------------------------------------*/

type ShipmentItems []ShipmentItem

type ShipmentItem struct {
	Code        string
	Price       float64
	Description string
	Quantity    int
}

func (v ShipmentItems) Value() (driver.Value, error) {
	return json.Marshal(&v)
}

func (v *ShipmentItems) Scan(value interface{}) error {
	return jsonScan(value, v, func() {
		*v = ShipmentItems{}
	})
}

/*--------------------------------------------------------------------------------------------------------------------*/

type ShipmentStatus string

const (
	ShipmentStatusFulfillmentRequestSent ShipmentStatus = "fulfillment_request_sent"
	ShipmentStatusCourierStaffAssigned   ShipmentStatus = "courier_staff_assigned"
	ShipmentStatusPickedUp               ShipmentStatus = "picked_up"
	ShipmentStatusOnDelivery             ShipmentStatus = "on_delivery"
	ShipmentStatusDelivered              ShipmentStatus = "delivered"
	ShipmentStatusFailedDelivery         ShipmentStatus = "failed_delivery"
	ShipmentStatusCancelled              ShipmentStatus = "cancelled"
)

const (
	CourierShipmentStatusFulfillmentRequestSent = "d1"
	CourierShipmentStatusCourierStaffAssigned   = "d2"
)

func (v ShipmentStatus) Value() string {
	return string(v)
}

type GetShipmentsParams struct {
	SearchKeyword            string
	WorkspaceID              *string
	Status                   *string
	Statuses                 []ShipmentStatus
	IDs                      []string
	CartIDs                  []string
	PickupPointID            *int64
	OnlyArchived             *bool
	CourierID                *int64
	Pagination               *common.Pagination
	Preload                  *[]string
	Fields                   *[]string
	CheckedAtBefore          *time.Time
	CourierTrackingIDNotNull bool
}

type GetShippingLabelsData struct {
	ID    string
	URL   string
	Error string
}

type GetShipmentStatusItem struct {
	Status        ShipmentStatus
	CourierStatus string
	StateText     string
	StateDate     time.Time
	CreatedAt     time.Time
}

type GetShipmentStatus struct {
	ID                string
	CourierTrackingID string
	StatusHistory     []*GetShipmentStatusItem
}

type DevUpdateShipmentParams struct {
	CourierTrackingID    string
	CourierStatus        string
	CancelReasonCode     string
	CancelReasonText     string
	CourierWebhookStatus string
}

type UpdateShipmentParams struct {
	PickupTime      *time.Time
	PickupStartTime *time.Time
	PickupEndTime   *time.Time
}

type CancelShipmentParams struct {
	Reason string
}

type ShipmentLabel struct {
	ID              string
	CourierCode     string
	TrackingNumber  string
	SenderName      string
	SenderPhone     string
	SenderAddress   string
	ReceiverName    string
	ReceiverPhone   string
	ReceiverAddress string
	Items           []*ShipmentLabelItem
	JnT             *ShipmentLabelJnT
	FlashExpress    *ShipmentLabelFlashExpress
}

type ShipmentLabelItem struct {
	Name     string
	Quantity uint32
}

type ShipmentLabelJnT struct {
	SortingCode     string
	StreetSortingNo string
}

type ShipmentLabelFlashExpress struct {
	SortingCode1    string
	SortingCode2    string
	SortingCode3    string
	SortingLineCode string
	DstStoreName    string
}

/*--------------------------------------------------------------------------------------------------------------------*/

var SortByArchivedAt SortBy = "archivedAt"
var SortByCreatedAt SortBy = "createdAt"

/*--------------------------------------------------------------------------------------------------------------------*/

type CourierShipmentStatus struct {
	CourierTrackingID string
	StatusCode        string
	StatusText        string
	StatusDate        time.Time
	IsNotFound        bool
}
