package main

import (
	"fmt"
	"github.com/spf13/viper"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"log"
	"sync"
	"time"

	"github.com/avast/retry-go"
)

func main() {

	type ShipmentStatus string

	type Shipment struct {
		ID                string
		CourierTrackingID string
		Status            ShipmentStatus
		CourierStatus     string
	}

	type UpdateStatusWebhookLog struct {
		ID                string
		CourierTrackingID string
		Status            ShipmentStatus
		CourierStatus     string
		CourierID         int64
		StateText         string
		StateDate         time.Time
	}

	const (
		ShipmentStatusPickedUp       ShipmentStatus = "picked_up"
		ShipmentStatusOnDelivery     ShipmentStatus = "on_delivery"
		ShipmentStatusDelivered      ShipmentStatus = "delivered"
		ShipmentStatusFailedDelivery ShipmentStatus = "failed_delivery"
	)

	var (
		db     *gorm.DB
		err    error
		config = struct {
			Host     string `mapstructure:"DB_HOST"`
			Username string `mapstructure:"DB_USERNAME"`
			Password string `mapstructure:"DB_PASSWORD"`
			Database string `mapstructure:"DB_DATABASE"`
			Port     string `mapstructure:"DB_PORT"`
		}{}
		FlashExpressMapOrderStatus = map[string]ShipmentStatus{
			"1": ShipmentStatusPickedUp,       // Picked up
			"2": ShipmentStatusOnDelivery,     // In transit
			"3": ShipmentStatusOnDelivery,     // On Delivery
			"4": ShipmentStatusOnDelivery,     // Detained
			"5": ShipmentStatusDelivered,      // Delivered
			"6": ShipmentStatusOnDelivery,     // Problematic Processing
			"7": ShipmentStatusFailedDelivery, // Returned
			"8": ShipmentStatusFailedDelivery, // Closed
			"9": ShipmentStatusFailedDelivery, // Cancelled
		}
	)

	viper.AutomaticEnv()
	viper.SetConfigFile(".env")
	viper.AddConfigPath(".")

	_ = viper.MergeInConfig()
	_ = viper.ReadInConfig()
	_ = viper.Unmarshal(&config)

	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable",
		config.Host,
		config.Username,
		config.Password,
		config.Database,
		config.Port,
	)

	err = retry.Do(func() error {
		db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
		return err
	},
		retry.Attempts(3),
		retry.Delay(time.Second),
	)

	if err != nil {
		log.Fatal(err)
	}

	var results []*Shipment
	if err := db.Model(&Shipment{}).
		Select("id, courier_tracking_id, status, courier_status").
		Where("courier_id = 3 AND status = 'courier_staff_assigned' AND courier_status NOT IN ('d1', 'd2') and courier_tracking_id is not null and courier_status is not null and courier_status <> ''").
		Find(&results).Error; err != nil {
		log.Fatal(err)
	}

	fmt.Println("")
	total := len(results)
	if total == 0 {
		fmt.Println("No records to update")
		return
	}

	wg := sync.WaitGroup{}
	wg.Add(total)
	for _, v := range results {
		v := v

		func() {
			var rows []*UpdateStatusWebhookLog
			if err := db.Model(&UpdateStatusWebhookLog{}).
				Where("courier_tracking_id = ? AND courier_status NOT IN ('d1', 'd2')", v.CourierTrackingID).
				Order("created_at DESC").Find(&rows).Error; err == nil {
				for i, row := range rows {
					if status, ok := FlashExpressMapOrderStatus[row.CourierStatus]; ok {
						if i == 0 {
							db.Model(&Shipment{}).Where("id = ?", v.ID).Update("status", status)
							fmt.Printf("Updated shipment `%s` from `%s` (%s) to `%s`\n", v.CourierTrackingID, v.Status, v.CourierStatus, status)
						}
						db.Model(&UpdateStatusWebhookLog{}).Where("id = ?", row.ID).Update("status", status)
						fmt.Printf("Updated webhook logs `%s` from `%s` (%s) to `%s`\n", row.CourierTrackingID, row.Status, row.CourierStatus, status)
					}
				}
				fmt.Println("")
			}
			wg.Done()
		}()
	}
	wg.Wait()

	fmt.Printf("Done: %d shipments has been updated successfully\n", total)
}
