package common

import (
	"encoding/json"
	"encoding/xml"
	"time"

	"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
)

type OrderUpdated struct {
	XMLName      xml.Name     `xml:"VehicleOrderReplicate"`
	Header       HeaderArea   `xml:"HeaderArea" validate:"required"`
	VehicleOrder VehicleOrder `xml:"DataArea>VehicleOrder" validate:"required"`
}

type HeaderArea struct {
	CreationDateTime  CreationDateTime `xml:"CreationDateTime" json:"creation_date_time"`
	MessageIdentifier string           `xml:"MessageIdentifier" json:"message_identifier"`
	InterfaceID       int              `xml:"InterfaceId" json:"interface_id"`
	SourceSystem      string           `xml:"Sender>SourceSystem" json:"source_system"`
	TargetSystem      string           `xml:"Sender>TargetSystem" json:"target_system"`
}

type VehicleOrder struct {
	SpecID               int                  `xml:"SpecId" json:"spec_id"`
	OrderNumber          int                  `xml:"VehicleOrderNumber" json:"order_number"`
	MessageIdentifier    string               `xml:"-" json:"message_identifier"`
	VehicleSpecification VehicleSpecification `xml:"VehicleSpecification" json:"vehicle_specification"`
}

type VehicleSpecification struct {
	OrderIndicator           string                `xml:"OrderIndicator" json:"order_indicator"`
	FleetOrderIndicator      string                `xml:"FleetOrderIndicator" json:"fleet_order_indicator"`
	ProductionPhaseIndicator string                `xml:"ProductionPhaseIndicator" json:"production_phase_indicator"`
	VehicleIndicator         string                `xml:"VehicleIndicator" json:"vehicle_indicator"`
	ManufacturingPlant       string                `xml:"ManufacturingPlant" json:"manufacturing_plant"`
	ExpectedReferenceDate    ExpectedReferenceDate `xml:"ExpectedReferenceDate" json:"expected_reference_date"`
	ModelType                string                `xml:"ModelType" json:"model_type"`
	ModelYearIndicator       int                   `xml:"ModelYearIndicator" json:"model_year_indicator"`
	ModelYear                int                   `xml:"ModelYear" json:"model_year"`
	SequenceNumber           string                `xml:"SequenceNumber" json:"sequence_number"`
	VersionDuringModelYear   int                   `xml:"VersionDuringModelYear" json:"version_during_model_year"`
	VehicleModel             string                `xml:"VehicleModel" json:"vehicle_model"`
	VinPrefix                string                `xml:"VinPrefix" json:"vin_prefix"`
	VehicleFeatures          []FeatureCodes        `xml:"VehicleFeatures>FeatureCodes" json:"vehicle_features"`
	DestinationCountry       string                `xml:"DestinationCountry" json:"destination_country"`
}

type FeatureCodes struct {
	FamilyCode  string `xml:"FamilyCode" json:"family_code"`
	FeatureCode string `xml:"FeatureCode" json:"feature_code"`
}

type ExpectedReferenceDate struct {
	time.Time
}

func (t *ExpectedReferenceDate) UnmarshalText(data []byte) error {
	// Fractional seconds are handled implicitly by Parse.
	var err error
	t.Time, err = time.Parse("2006-01-02", string(data))

	return err
}

func (t ExpectedReferenceDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	s := t.Format("2006-01-02")
	return e.EncodeElement(s, start)
}

type CreationDateTime struct {
	time.Time
}

func (t *CreationDateTime) UnmarshalText(data []byte) error {
	// Fractional seconds are handled implicitly by Parse.
	var err error
	t.Time, err = time.Parse("2006-01-02T15:04", string(data))

	return err
}

func (t CreationDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	s := t.Format("2006-01-02T15:04")
	return e.EncodeElement(s, start)
}

// OrderUpdatedSwag is used ONLY to represent OrderUpdated in swagger.
type OrderUpdatedSwag struct {
	Header struct {
		CreationDateTime  CreationDateTime `json:"CreationDateTime" swaggertype:"string"`
		MessageIdentifier string           `json:"MessageIdentifier"`
		InterfaceID       int              `json:"InterfaceId"`
		Sender            struct {
			SourceSystem string `json:"SourceSystem"`
			TargetSystem string `json:"TargetSystem"`
		} `json:"Sender"`
	} `json:"HeaderArea" validate:"required"`
	DataArea struct {
		VehicleOrder struct {
			SpecID               int `json:"SpecId"`
			OrderNumber          int `json:"VehicleOrderNumber"`
			VehicleSpecification struct {
				OrderIndicator           string                `json:"OrderIndicator"`
				FleetOrderIndicator      string                `json:"FleetOrderIndicator"`
				ProductionPhaseIndicator string                `json:"ProductionPhaseIndicator"`
				VehicleIndicator         string                `json:"VehicleIndicator"`
				ManufacturingPlant       string                `json:"ManufacturingPlant"`
				ExpectedReferenceDate    ExpectedReferenceDate `json:"ExpectedReferenceDate" swaggertype:"string"`
				ModelType                string                `json:"ModelType"`
				ModelYearIndicator       int                   `json:"ModelYearIndicator"`
				SequenceNumber           string                `json:"SequenceNumber"`
				VehicleModel             string                `json:"VehicleModel"`
				VinPrefix                string                `json:"VinPrefix"`
				VehicleFeatures          []struct {
					FeatureCodes struct {
						FamilyCode  string `json:"FamilyCode" `
						FeatureCode string `json:"FeatureCode"`
					} `json:"FeatureCodes"`
				} `json:"VehicleFeatures"`
				DestinationCountry string `json:"DestinationCountry"`
			} `json:"VehicleSpecification"`
		} `json:"VehicleOrder" validate:"required"`
	} `json:"DataArea"`
} // @name VehicleOrderReplicate

func OrderToGRPC(input MessageRawJSON) *kafka_grpc.GRPC_AttendantPayload {
	grpcPayload := &kafka_grpc.GRPC_AttendantPayload{
		Handler: input.Handler,
	}
	var order VehicleOrder
	err := json.Unmarshal(input.Data, &order)
	if err != nil {
		return grpcPayload
	}
	orderObj := &kafka_grpc.Order{
		VehicleSpecification: &kafka_grpc.VehicleSpecification{},
	}
	orderObj.VehicleSpecification.Date = order.VehicleSpecification.ExpectedReferenceDate.UnixMilli()
	orderObj.MsgIdentifier = order.MessageIdentifier
	orderObj.SpecId = int64(order.SpecID)
	orderObj.VehicleSpecification.OrderId = order.VehicleSpecification.OrderIndicator
	orderObj.VehicleSpecification.FOrderId = order.VehicleSpecification.FleetOrderIndicator
	orderObj.VehicleSpecification.MfPlant = order.VehicleSpecification.ManufacturingPlant
	orderObj.VehicleSpecification.DestCon = order.VehicleSpecification.DestinationCountry
	orderObj.VehicleSpecification.ModelType = order.VehicleSpecification.ModelType
	orderObj.VehicleSpecification.ProductId = order.VehicleSpecification.ProductionPhaseIndicator
	orderObj.VehicleSpecification.Sn = order.VehicleSpecification.SequenceNumber
	orderObj.VehicleSpecification.VehicleId = order.VehicleSpecification.VehicleIndicator
	orderObj.VehicleSpecification.Model = order.VehicleSpecification.VehicleModel
	orderObj.VehicleSpecification.VinPre = order.VehicleSpecification.VinPrefix
	orderObj.VehicleSpecification.ModelYear = int64(order.VehicleSpecification.ModelYear)
	orderObj.VehicleSpecification.ModelId = int64(order.VehicleSpecification.ModelYearIndicator)
	orderObj.VehicleSpecification.Version = int64(order.VehicleSpecification.VersionDuringModelYear)

	if len(order.VehicleSpecification.VehicleFeatures) > 0 {
		var features []*kafka_grpc.FeatureCodes
		for _, feature := range order.VehicleSpecification.VehicleFeatures {
			features = append(features, &kafka_grpc.FeatureCodes{
				FamilyCode:  feature.FamilyCode,
				FeatureCode: feature.FeatureCode,
			})
		}
		orderObj.VehicleSpecification.Feature = features
	}

	payload := &kafka_grpc.GRPC_AttendantPayload_Order{
		Order: orderObj,
	}
	grpcPayload.Data = payload
	return grpcPayload
}

func GRPCToOrder(payload *kafka_grpc.GRPC_AttendantPayload) *VehicleOrder {
	if payload.Data == nil {
		return nil
	}
	data := payload.Data.(*kafka_grpc.GRPC_AttendantPayload_Order)
	order := &VehicleOrder{}
	order.OrderNumber = int(data.Order.OrderNo)
	order.MessageIdentifier = data.Order.MsgIdentifier
	order.SpecID = int(data.Order.SpecId)
	order.VehicleSpecification = VehicleSpecification{}
	if data.Order.VehicleSpecification != nil {
		order.VehicleSpecification.DestinationCountry = data.Order.VehicleSpecification.DestCon
		order.VehicleSpecification.FleetOrderIndicator = data.Order.VehicleSpecification.FOrderId
		order.VehicleSpecification.FleetOrderIndicator = data.Order.VehicleSpecification.FOrderId

		order.VehicleSpecification.ManufacturingPlant = data.Order.VehicleSpecification.MfPlant

		order.VehicleSpecification.ModelType = data.Order.VehicleSpecification.ModelType

		order.VehicleSpecification.ModelYear = int(data.Order.VehicleSpecification.ModelYear)

		order.VehicleSpecification.ModelYearIndicator = int(data.Order.VehicleSpecification.ModelId)

		order.VehicleSpecification.OrderIndicator = data.Order.VehicleSpecification.OrderId

		order.VehicleSpecification.ProductionPhaseIndicator = data.Order.VehicleSpecification.ProductId

		order.VehicleSpecification.FleetOrderIndicator = data.Order.VehicleSpecification.FOrderId

		order.VehicleSpecification.SequenceNumber = data.Order.VehicleSpecification.Sn

		order.VehicleSpecification.VehicleIndicator = data.Order.VehicleSpecification.VehicleId

		order.VehicleSpecification.VehicleModel = data.Order.VehicleSpecification.Model

		order.VehicleSpecification.VinPrefix = data.Order.VehicleSpecification.VinPre

		order.VehicleSpecification.VersionDuringModelYear = int(data.Order.VehicleSpecification.Version)

		seconds := data.Order.VehicleSpecification.Date / 1000
		nanoseconds := (data.Order.VehicleSpecification.Date % 1000) * int64(time.Millisecond)
		order.VehicleSpecification.ExpectedReferenceDate = ExpectedReferenceDate{time.Unix(seconds, nanoseconds)}

		if data.Order.VehicleSpecification.Feature != nil && len(data.Order.VehicleSpecification.Feature) > 0 {
			var features []FeatureCodes
			for _, feature := range data.Order.VehicleSpecification.Feature {
				features = append(features, FeatureCodes{
					FamilyCode:  feature.FamilyCode,
					FeatureCode: feature.FeatureCode,
				})
			}
			order.VehicleSpecification.VehicleFeatures = features
		}
	}

	return order
}
