package distribution

import (
	"time"

	"github.com/jackc/pgx/v5/pgtype"
	"github.com/shopspring/decimal"

	"github.com/TaruLab/keluargamart-backend/internal/db"
)

// --- rows -> views -----------------------------------------------------------

func deliveryOrderFromRow(r db.GetDeliveryOrderByIDRow) *DeliveryOrder {
	return &DeliveryOrder{
		ID:                r.ID,
		DoNumber:          r.DoNumber,
		SalesOrderID:      r.SalesOrderID,
		CustomerID:        r.CustomerID,
		WarehouseID:       r.WarehouseID,
		DoDate:            r.DoDate,
		Priority:          r.Priority,
		Status:            r.Status,
		RequestedQuantity: r.RequestedQuantity,
		ShippedQuantity:   r.ShippedQuantity,
		DeliveredQuantity: r.DeliveredQuantity,
		RemainingQuantity: r.RemainingQuantity,
		Notes:             textToStrPtr(r.Notes),
		CreatedBy:         int8ToPtr(r.CreatedBy),
		CustomerName:      r.CustomerName,
		SoNumber:          r.SoNumber,
		ShippingAddress:   textToStrPtr(r.ShippingAddress),
		SalesName:         textToStrPtr(r.SalesName),
		RecipientName:     r.RecipientName,
		ProofPhotoURL:     textToStrPtr(r.ProofPhotoUrl),
		PodRecipient:      textToStrPtr(r.PodRecipientName),
		DeliveredAt:       tstzToStrPtr(r.DeliveredAt),
		CreatedAt:         r.CreatedAt.Time,
		UpdatedAt:         r.UpdatedAt.Time,
	}
}

func deliveryOrderFromListRow(r db.ListDeliveryOrdersRow) DeliveryOrder {
	return *deliveryOrderFromRow(db.GetDeliveryOrderByIDRow(r))
}

func deliveryOrderFromLockedRow(r db.GetDeliveryOrderByIDForUpdateRow) *DeliveryOrder {
	return deliveryOrderFromRow(db.GetDeliveryOrderByIDRow(r))
}

func itemFromRow(r db.ListDeliveryOrderItemsRow) DeliveryOrderItem {
	return DeliveryOrderItem{
		ID:                r.ID,
		DeliveryOrderID:   r.DeliveryOrderID,
		ProductID:         r.ProductID,
		RequestedQuantity: r.RequestedQuantity,
		ShippedQuantity:   r.ShippedQuantity,
		DeliveredQuantity: r.DeliveredQuantity,
		RemainingQuantity: r.RemainingQuantity,
		ProductCode:       r.Code,
		ProductName:       r.Name,
		UnitPrice:         r.UnitPrice,
		Subtotal:          r.Subtotal,
	}
}

func itemFromLockedRow(r db.DeliveryOrderItem) DeliveryOrderItem {
	return DeliveryOrderItem{
		ID:                r.ID,
		DeliveryOrderID:   r.DeliveryOrderID,
		ProductID:         r.ProductID,
		RequestedQuantity: r.RequestedQuantity,
		ShippedQuantity:   r.ShippedQuantity,
		DeliveredQuantity: r.DeliveredQuantity,
		RemainingQuantity: r.RemainingQuantity,
	}
}

func shipmentFromRow(r db.GetShipmentByIDRow) *Shipment {
	return &Shipment{
		ID:              r.ID,
		ShipmentNumber:  r.ShipmentNumber,
		TrackingNumber:  r.TrackingNumber.String,
		Barcode:         r.Barcode.String,
		DeliveryOrderID: r.DeliveryOrderID,
		Status:          r.Status,
		ShipDate:        r.ShipDate,
		Notes:           textToStrPtr(r.Notes),
		CreatedBy:       int8ToPtr(r.CreatedBy),
		CreatedAt:       r.CreatedAt.Time,
		UpdatedAt:       r.UpdatedAt.Time,
	}
}

func shipmentFromLockedRow(r db.GetShipmentByIDForUpdateRow) *Shipment {
	return shipmentFromRow(db.GetShipmentByIDRow(r))
}

func shipmentFromListRow(r db.ListShipmentsByDeliveryOrderRow) Shipment {
	return *shipmentFromRow(db.GetShipmentByIDRow(r))
}

func shipmentFromNumberRow(r db.GetShipmentByShipmentNumberRow) *Shipment {
	return shipmentFromRow(db.GetShipmentByIDRow(r))
}

func shipmentFromTrackingRow(r db.GetShipmentByTrackingNumberRow) *Shipment {
	return shipmentFromRow(db.GetShipmentByIDRow(r))
}

func shipmentFromBarcodeRow(r db.GetShipmentByBarcodeRow) *Shipment {
	return shipmentFromRow(db.GetShipmentByIDRow(r))
}

func shipmentFromDONumberRow(r db.ListShipmentsByDeliveryOrderNumberRow) *Shipment {
	return &Shipment{
		ID:              r.ID,
		ShipmentNumber:  r.ShipmentNumber,
		TrackingNumber:  r.TrackingNumber.String,
		Barcode:         r.Barcode.String,
		DeliveryOrderID: r.DeliveryOrderID,
		Status:          r.Status,
		ShipDate:        r.SShipDate,
		Notes:           textToStrPtr(r.Notes),
		CreatedBy:       int8ToPtr(r.CreatedBy),
		CreatedAt:       r.CreatedAt.Time,
		UpdatedAt:       r.UpdatedAt.Time,
	}
}

func trackingEventFromRow(r db.ShipmentTrackingEvent) TrackingEvent {
	return TrackingEvent{
		ID:          r.ID,
		ShipmentID:  r.ShipmentID,
		EventTime:   r.EventTime.Time,
		Status:      r.Status,
		Source:      r.Source,
		Latitude:    nullDecimalToFloatPtr(r.Latitude),
		Longitude:   nullDecimalToFloatPtr(r.Longitude),
		AccuracyM:   nullDecimalToFloatPtr(r.AccuracyM),
		SpeedMps:    nullDecimalToFloatPtr(r.SpeedMps),
		HeadingDeg:  nullDecimalToFloatPtr(r.HeadingDeg),
		Note:        textToStrPtr(r.Note),
		ProviderRef: textToStrPtr(r.ProviderRef),
		CreatedBy:   int8ToPtr(r.CreatedBy),
		CreatedAt:   r.CreatedAt.Time,
	}
}

func createTrackingEventParams(e *TrackingEvent) db.CreateShipmentTrackingEventParams {
	return db.CreateShipmentTrackingEventParams{
		ShipmentID:  e.ShipmentID,
		EventTime:   pgtype.Timestamptz{Time: e.EventTime, Valid: true},
		Status:      e.Status,
		Source:      e.Source,
		Latitude:    floatPtrToNullDecimal(e.Latitude),
		Longitude:   floatPtrToNullDecimal(e.Longitude),
		AccuracyM:   floatPtrToNullDecimal(e.AccuracyM),
		SpeedMps:    floatPtrToNullDecimal(e.SpeedMps),
		HeadingDeg:  floatPtrToNullDecimal(e.HeadingDeg),
		Note:        textOrNilStr(e.Note),
		ProviderRef: textOrNilStr(e.ProviderRef),
		CreatedBy:   int8OrNil(e.CreatedBy),
	}
}

func shipmentItemFromRow(r db.ListShipmentItemsRow) ShipmentItem {
	return ShipmentItem{
		ID:                  r.ID,
		ShipmentID:          r.ShipmentID,
		DeliveryOrderItemID: r.DeliveryOrderItemID,
		ProductID:           r.ProductID,
		ShippedQuantity:     r.ShippedQuantity,
		DeliveredQuantity:   r.DeliveredQuantity,
		ProductCode:         r.Code,
		ProductName:         r.Name,
	}
}

func shipmentItemFromLockedRow(r db.ShipmentItem) ShipmentItem {
	return ShipmentItem{
		ID:                  r.ID,
		ShipmentID:          r.ShipmentID,
		DeliveryOrderItemID: r.DeliveryOrderItemID,
		ProductID:           r.ProductID,
		ShippedQuantity:     r.ShippedQuantity,
		DeliveredQuantity:   r.DeliveredQuantity,
	}
}

func dispatchFromRow(r db.Dispatch) Dispatch {
	return Dispatch{
		ID:              r.ID,
		DeliveryOrderID: r.DeliveryOrderID,
		ShipmentID:      r.ShipmentID,
		FleetID:         r.FleetID,
		Priority:        r.Priority,
		QueueOrder:      r.QueueOrder,
		DriverID:        int8ToPtr(r.DriverID),
		ScheduledFor:    tstzToStrPtr(r.ScheduledFor),
		Status:          r.Status,
		DispatchedAt:    tstzToStrPtr(r.DispatchedAt),
		DispatcherID:    int8ToPtr(r.DispatcherID),
		CreatedAt:       r.CreatedAt.Time,
		UpdatedAt:       r.UpdatedAt.Time,
	}
}

func vehicleFromRow(r db.Fleet) Vehicle {
	return Vehicle{
		ID:             r.ID,
		PlateNumber:    r.PlateNumber,
		VehicleName:    r.VehicleName,
		VehicleType:    textToStrPtr(r.VehicleType),
		CapacityWeight: nullDecimalToPtr(r.CapacityWeight),
		CapacityVolume: nullDecimalToPtr(r.CapacityVolume),
		IsActive:       r.IsActive,
		CreatedAt:      r.CreatedAt.Time,
		UpdatedAt:      r.UpdatedAt.Time,
	}
}

// --- views -> params ---------------------------------------------------------

func createDeliveryOrderParams(o *DeliveryOrder) db.CreateDeliveryOrderParams {
	return db.CreateDeliveryOrderParams{
		DoNumber:          o.DoNumber,
		SalesOrderID:      o.SalesOrderID,
		CustomerID:        o.CustomerID,
		WarehouseID:       o.WarehouseID,
		DoDate:            dateOrNil(o.DoDate),
		Priority:          o.Priority,
		Status:            o.Status,
		RequestedQuantity: o.RequestedQuantity,
		ShippedQuantity:   o.ShippedQuantity,
		DeliveredQuantity: o.DeliveredQuantity,
		RemainingQuantity: o.RemainingQuantity,
		Notes:             textOrNilStr(o.Notes),
		CreatedBy:         int8OrNil(o.CreatedBy),
	}
}

func createDeliveryOrderItemParams(it *DeliveryOrderItem) db.CreateDeliveryOrderItemParams {
	return db.CreateDeliveryOrderItemParams{
		DeliveryOrderID:   it.DeliveryOrderID,
		ProductID:         it.ProductID,
		RequestedQuantity: it.RequestedQuantity,
		ShippedQuantity:   it.ShippedQuantity,
		DeliveredQuantity: it.DeliveredQuantity,
		RemainingQuantity: it.RemainingQuantity,
	}
}

func createShipmentParams(sh *Shipment) db.CreateShipmentParams {
	return db.CreateShipmentParams{
		ShipmentNumber:  sh.ShipmentNumber,
		DeliveryOrderID: sh.DeliveryOrderID,
		Status:          sh.Status,
		ShipDate:        dateOrNil(sh.ShipDate),
		Notes:           textOrNilStr(sh.Notes),
		CreatedBy:       int8OrNil(sh.CreatedBy),
		TrackingNumber:  textOrNilStr(&sh.TrackingNumber),
		Barcode:         textOrNilStr(&sh.Barcode),
	}
}

func createShipmentItemParams(it *ShipmentItem) db.CreateShipmentItemParams {
	return db.CreateShipmentItemParams{
		ShipmentID:          it.ShipmentID,
		DeliveryOrderItemID: it.DeliveryOrderItemID,
		ProductID:           it.ProductID,
		ShippedQuantity:     it.ShippedQuantity,
	}
}

func createStatusHistoryParams(h *DeliveryStatusHistory) db.CreateDeliveryStatusHistoryParams {
	return db.CreateDeliveryStatusHistoryParams{
		DeliveryOrderID: h.DeliveryOrderID,
		FromStatus:      h.FromStatus,
		ToStatus:        h.ToStatus,
		Reason:          h.Reason,
		ReferenceType:   textOrNilStr(h.ReferenceType),
		ReferenceID:     int8OrNil(h.ReferenceID),
		ChangedBy:       int8OrNil(h.ChangedBy),
	}
}

func createDispatchParams(d *Dispatch) db.CreateDispatchParams {
	return db.CreateDispatchParams{
		DeliveryOrderID: d.DeliveryOrderID,
		ShipmentID:      d.ShipmentID,
		FleetID:         d.FleetID,
		Priority:        d.Priority,
		QueueOrder:      d.QueueOrder,
		DriverID:        int8OrNil(d.DriverID),
		ScheduledFor:    tstzOrNil(d.ScheduledFor),
		Status:          d.Status,
		DispatchedAt:    tstzOrNil(d.DispatchedAt),
		DispatcherID:    int8OrNil(d.DispatcherID),
	}
}

func createFleetParams(v *Vehicle) db.CreateFleetParams {
	return db.CreateFleetParams{
		PlateNumber:    v.PlateNumber,
		VehicleName:    v.VehicleName,
		VehicleType:    textOrNilStr(v.VehicleType),
		CapacityWeight: decimalOrNull(v.CapacityWeight),
		CapacityVolume: decimalOrNull(v.CapacityVolume),
		IsActive:       v.IsActive,
	}
}

// --- pgtype helpers ----------------------------------------------------------

func int8ToPtr(v pgtype.Int8) *int64 {
	if !v.Valid {
		return nil
	}
	x := v.Int64
	return &x
}

func int8OrNil(v *int64) pgtype.Int8 {
	if v == nil {
		return pgtype.Int8{}
	}
	return pgtype.Int8{Int64: *v, Valid: true}
}

func int8(v int64) pgtype.Int8 {
	return pgtype.Int8{Int64: v, Valid: true}
}

func textToStrPtr(v pgtype.Text) *string {
	if !v.Valid {
		return nil
	}
	s := v.String
	return &s
}

func textOrNilStr(v *string) pgtype.Text {
	if v == nil {
		return pgtype.Text{}
	}
	return pgtype.Text{String: *v, Valid: true}
}

func dateOrNil(v string) pgtype.Date {
	if v == "" {
		return pgtype.Date{}
	}
	if t, err := time.Parse("2006-01-02", v); err == nil {
		return pgtype.Date{Time: t, Valid: true}
	}
	return pgtype.Date{}
}

func tstzToStrPtr(v pgtype.Timestamptz) *string {
	if !v.Valid {
		return nil
	}
	s := v.Time.UTC().Format(time.RFC3339)
	return &s
}

func tstzOrNil(v *string) pgtype.Timestamptz {
	if v == nil || *v == "" {
		return pgtype.Timestamptz{}
	}
	if t, err := time.Parse(time.RFC3339, *v); err == nil {
		return pgtype.Timestamptz{Time: t, Valid: true}
	}
	return pgtype.Timestamptz{}
}

func nullDecimalToPtr(v decimal.NullDecimal) *decimal.Decimal {
	if !v.Valid {
		return nil
	}
	d := v.Decimal
	return &d
}

func decimalOrNull(v *decimal.Decimal) decimal.NullDecimal {
	if v == nil {
		return decimal.NullDecimal{}
	}
	return decimal.NullDecimal{Decimal: *v, Valid: true}
}

func nullDecimalToFloatPtr(v decimal.NullDecimal) *float64 {
	if !v.Valid {
		return nil
	}
	f, _ := v.Decimal.Float64()
	return &f
}

func floatPtrToNullDecimal(v *float64) decimal.NullDecimal {
	if v == nil {
		return decimal.NullDecimal{}
	}
	return decimal.NullDecimal{Decimal: decimal.NewFromFloat(*v), Valid: true}
}

// --- tracking event row adapters (sqlc row structs per query) ----------------

func eventFromListRow(r db.ListShipmentTrackingEventsRow) TrackingEvent {
	return TrackingEvent{
		ID: r.ID, ShipmentID: r.ShipmentID,
		EventTime:  r.EventTime.Time,
		Status:     r.Status,
		Source:     r.Source,
		Latitude:   nullDecimalToFloatPtr(r.Latitude),
		Longitude:  nullDecimalToFloatPtr(r.Longitude),
		AccuracyM:  nullDecimalToFloatPtr(r.AccuracyM),
		SpeedMps:   nullDecimalToFloatPtr(r.SpeedMps),
		HeadingDeg: nullDecimalToFloatPtr(r.HeadingDeg),
		Note:       textToStrPtr(r.Note), ProviderRef: textToStrPtr(r.ProviderRef),
		CreatedBy: int8ToPtr(r.CreatedBy), CreatedAt: r.CreatedAt.Time,
	}
}

func eventFromProviderRefRow(r db.GetShipmentTrackingEventByProviderRefRow) TrackingEvent {
	return eventFromListRow(db.ListShipmentTrackingEventsRow(r))
}
