package encounter

import (
	pl "simrs-vx/pkg/logger"

	d "github.com/karincake/dodol"
	"gorm.io/gorm"

	db "simrs-vx/pkg/dualtrx-helper"

	e "simrs-vx/internal/domain/main-entities/encounter"
	etp "simrs-vx/internal/domain/simgos-entities/t-pendaftaran"
	synce "simrs-vx/internal/domain/sync-entities/encounter"
	syncir "simrs-vx/internal/domain/sync-entities/internal-reference"
	elog "simrs-vx/internal/domain/sync-entities/log"

	utph "simrs-vx/internal/use-case/simgos-sync-use-case/new/internal-reference"
)

const source = "encounter"

func Create(input e.Encounter) (*d.Data, error) {
	var (
		sgData   *etp.TPendaftaran
		syncLink *synce.EncounterLink
		err      error
	)

	event := pl.Event{
		Feature: "Create",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "create")

	err = db.WithDualTx(func(tx *db.Dualtx) error {
		// STEP 1: Insert to simgos
		sgData, err = CreateTPendaftaranData(input, &event, tx.Simgos)
		if err != nil {
			return err
		}

		// STEP 2: Insert to Link
		syncLink, err = CreateLinkData(input.Id, sgData.Idxdaftar, &event, tx.Sync)
		if err != nil {
			return err
		}

		// STEP 3: Update MPasien
		err = updatePatientCaraBayar(*sgData, &event, tx.Simgos)
		if err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		if syncLink != nil {
			go func() { _ = DeleteLinkData(syncLink, &event) }()
		}
		return nil, err
	}

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.II{
			"source":    source,
			"structure": "single-data",
			"status":    "created",
		},
	}, nil
}

func CreateSimxLog(input elog.SimxLogDto) (*d.Data, error) {
	event := pl.Event{
		Feature: "Create",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "create")

	tx := db.NewTx()
	err := tx.Sync.Transaction(func(tx *gorm.DB) error {
		// Insert to Log
		if err := CreateLogData(input, &event, tx); err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.II{
			"source":    source,
			"structure": "single-data",
			"status":    "created",
		},
	}, nil
}

func Update(input e.Encounter) (*d.Data, error) {
	event := pl.Event{
		Feature: "Update",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "update")

	// STEP 1: Get Link
	syncLink, err := ReadDetailLinkData(input.Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get Simgos
	simgos, err := ReadTPendaftaranDetailData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	tx := db.NewTx()
	err = tx.Simgos.Transaction(func(tx *gorm.DB) error {
		// Step 2: Update Simgos
		if err = UpdateTPendaftaranData(input, simgos, "update", &event, tx); err != nil {
			return err
		}

		return nil
	})

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}

func Delete(input e.DeleteDto) (*d.Data, error) {
	var isLinkDeleted bool

	event := pl.Event{
		Feature: "Delete",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "delete")

	// STEP 1: Get Link
	syncLink, err := ReadDetailLinkData(input.Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get Simgos
	sgData, err := ReadTPendaftaranDetailData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	err = db.WithDualTx(func(tx *db.Dualtx) error {
		// STEP 3: Delete Simgos
		err = HardDeleteTPendaftaranData(sgData, &event, tx.Simgos)
		if err != nil {
			return err
		}

		// STEP 4: Delete Link
		err = DeleteLinkData(syncLink, &event, tx.Sync)
		if err != nil {
			return err
		}

		isLinkDeleted = true
		return nil
	})

	if err != nil {
		if isLinkDeleted {
			go func() {
				_, _ = CreateLinkData(uint(input.Id), sgData.Idxdaftar, &event)
			}()
		}
		return nil, err
	}

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "deleted",
		},
	}, nil

}

func CheckIn(input e.Encounter) (*d.Data, error) {
	event := pl.Event{
		Feature: "CheckIn",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "check-in")

	// STEP 1: Get Link
	syncLink, err := ReadDetailLinkData(input.Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get Simgos
	simgos, err := ReadTPendaftaranDetailData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	tx := db.NewTx()
	err = tx.Simgos.Transaction(func(tx *gorm.DB) error {
		// Step 2: Update Simgos
		if err = UpdateTPendaftaranData(input, simgos, "check-in", &event, tx); err != nil {
			return err
		}

		return nil
	})

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}

func CheckOut(input e.Encounter) (*d.Data, error) {
	event := pl.Event{
		Feature: "CheckOut",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "check-out")

	// STEP 1: Get Link
	syncLink, err := ReadDetailLinkData(input.Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get Simgos
	simgos, err := ReadTPendaftaranDetailData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	tx := db.NewTx()
	err = tx.Simgos.Transaction(func(tx *gorm.DB) error {
		// Step 2: Update Simgos
		if err = UpdateTPendaftaranData(input, simgos, "check-out", &event, tx); err != nil {
			return err
		}

		// TODO: Update TPemeriksaanHistory end_konsul if internal reference exist

		return nil
	})

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}

func UpdateStatus(input e.Encounter) (*d.Data, error) {
	event := pl.Event{
		Feature: "Cancel",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "cancel")

	// STEP 1: Get Link
	syncLink, err := ReadDetailLinkData(input.Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get Simgos
	simgos, err := ReadTPendaftaranDetailData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	tx := db.NewTx()
	err = tx.Simgos.Transaction(func(tx *gorm.DB) error {
		// Step 2: Update Simgos
		if err = UpdateTPendaftaranData(input, simgos, "update-status", &event, tx); err != nil {
			return err
		}

		return nil
	})

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}

func RequestSwitchUnit(input e.Encounter) (*d.Data, error) {
	var (
		syncLinkInternal *[]syncir.InternalReferenceLink
	)

	event := pl.Event{
		Feature: "RequestSwitchUnit",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "request-switch-unit")

	// STEP 1: Get Link
	syncLink, err := ReadDetailLinkData(input.Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get Simgos
	tpendaftaran, err := ReadTPendaftaranDetailData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	err = db.WithDualTx(func(tx *db.Dualtx) error {
		// Step 3: Update TPendaftaran
		if err = UpdateTPendaftaranData(input, tpendaftaran, "update-status", &event, tx.Simgos); err != nil {
			return err
		}

		// Step 4: Bulk Insert TPemeriksaanHist
		tPemeriksanHistory, err := utph.CreateSimgosData(input, tpendaftaran, &event, tx.Simgos)
		if err != nil {
			return err
		}

		// Step 5: Bulk Insert To Link
		if syncLinkInternal, err = utph.CreateBulkLinkData(*input.InternalReferences, *tPemeriksanHistory, &event, tx.Sync); err != nil {
			return err
		}

		return nil
	})

	if err != nil {
		if syncLinkInternal != nil {
			go func() { _ = utph.DeleteLinkData(syncLinkInternal, &event) }()
		}
		return nil, err
	}

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}

func ApproveSwitchUnit(input e.ApproveCancelUnitDto) (*d.Data, error) {
	event := pl.Event{
		Feature: "ApproveSwitchUnit",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "approve-switch-unit")

	// STEP 1: Get InternalReference Link
	syncLink, err := utph.ReadDetailLinkData(input.InternalReferences_Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get TPemeriksaanHist
	tPemeriksaanHist, err := utph.ReadDetailSimgosData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	tx := db.NewTx()
	err = tx.Simgos.Transaction(func(tx *gorm.DB) error {
		// Step 3: Update TPemeriksaanHist
		if err := utph.UpdateSimgosData(input, tPemeriksaanHist, "approve", &event, tx); err != nil {
			return err
		}

		return nil
	})

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}

func CancelSwitchUnit(input e.ApproveCancelUnitDto) (*d.Data, error) {
	event := pl.Event{
		Feature: "CancelSwitchUnit",
		Source:  source,
	}

	// Start log
	pl.SetLogInfo(&event, input, "started", "cancel-switch-unit")

	// STEP 1: Get InternalReference Link
	syncLink, err := utph.ReadDetailLinkData(input.InternalReferences_Id, &event)
	if err != nil {
		return nil, err
	}

	// STEP 2: Get TPemeriksaanHist
	tPemeriksaanHist, err := utph.ReadDetailSimgosData(syncLink.Simgos_Id, &event)
	if err != nil {
		return nil, err
	}

	tx := db.NewTx()
	err = tx.Simgos.Transaction(func(tx *gorm.DB) error {
		// Step 3: Update TPemeriksaanHist
		if err := utph.UpdateSimgosData(input, tPemeriksaanHist, "cancel", &event, tx); err != nil {
			return err
		}

		return nil
	})

	pl.SetLogInfo(&event, nil, "complete")

	return &d.Data{
		Meta: d.IS{
			"source":    source,
			"structure": "single-data",
			"status":    "updated",
		},
	}, nil
}
