package imagingstudy

import (
	"context"
	"database/sql"
	"service/internal/infrastructure/database"
	"service/pkg/logger"
	"service/pkg/utils/query"
	"time"
)

type Repository interface {
	GetNextImagingStudy(ctx context.Context, lastCreatedAt time.Time) (*ImagingStudyDB, error)
	SaveSatuSehatID(ctx context.Context, sourceID string, fhirID string) error
	SaveSyncLog(ctx context.Context, logData SyncLog) error
}

type repository struct {
	dbManager database.Service
}

func NewRepository(dbManager database.Service) Repository {
	return &repository{dbManager: dbManager}
}

// GetNextImagingStudy mengambil data imaging study berikutnya untuk diproses.
// Tracker `lastID` adalah primary key integer (`id`) dari tabel `data_service_requests`.
// Data akan diambil mulai dari tanggal 2026-04-15 dan melompati `lastCreatedAt` yang sudah diproses.
func (r *repository) GetNextImagingStudy(ctx context.Context, lastCreatedAt time.Time) (*ImagingStudyDB, error) {
	var data ImagingStudyDB

	// 1. Inisialisasi Koneksi ke Database yang diperlukan
	satudataDB, err := r.dbManager.GetSQLXDB("satudata")
	if err != nil {
		return nil, err
	}
	risDB, err := r.dbManager.GetSQLXDB("ris")
	if err != nil {
		return nil, err
	}

	// --- Langkah 1: Ambil data pemicu dari `satudata` ---
	qbSatuData := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)

	// Struct penampung sementara untuk mendapat foreign key ke db lain
	type BaseSatuData struct {
		ID               string         `db:"id"` // Perbaikan: Ubah ke string untuk menampung UUID
		ServiceRequestID sql.NullString `db:"service_request_id"`
		EncounterIHS     sql.NullString `db:"encounter_ihs"`
		PatientIHS       sql.NullString `db:"patient_ihs"`
		NoRegister       sql.NullString `db:"No_register"`
		TanggalOrder     sql.NullTime   `db:"Tanggal_order"`
		NoMR             sql.NullString `db:"nomr"`
		CreatedAt        time.Time      `db:"Created_at"`
		PractitionerIHS  sql.NullString `db:"practitioner_ihs"`
		StartedDate      sql.NullTime   `db:"started_date"`
	}
	var baseData BaseSatuData

	// Hardcode tanggal mulai
	startDate, _ := time.Parse("2006-01-02", "2026-01-01")

	qSatuData := query.DynamicQuery{
		From:    "public.data_service_requests",
		Aliases: "dsr",
		Fields: []query.SelectField{
			{Expression: `dsr.id`},
			{Expression: `dsr."Entity_Id"`, Alias: "service_request_id"},
			{Expression: `dsr."Encounter_Id"`, Alias: "encounter_ihs"},
			{Expression: `dsr."No_register"`},
			{Expression: `dsr."Tanggal_order"`},
			{Expression: `dsr."Occurrence_date_time"`, Alias: "started_date"},
			{Expression: `dsr."Requester_reference"`, Alias: "practitioner_ihs"},
			{Expression: `dsr."Created_at"`},
			{Expression: `dkp."Nomor_satusehat_pasien"`, Alias: "patient_ihs"},
			{Expression: `dkp."NOMR"`, Alias: "nomr"},
		},
		Joins: []query.Join{
			{
				Type:  "LEFT",
				Table: "public.data_kunjungan_pasien",
				Alias: "dkp",
				OnConditions: query.CreateAndFilterGroup([]query.DynamicFilter{
					query.CreateFilter(`dkp."IDXDAFTAR_satusehat"`, query.OpEqual, `dsr."Encounter_Id"`),
				}),
			},
		},
		Filters: []query.FilterGroup{
			{Filters: []query.DynamicFilter{
				query.CreateFilter(`dsr."Created_at"`, query.OpGreaterThan, lastCreatedAt),
				query.CreateFilter(`dsr."Tanggal_order"`, query.OpGreaterThanEqual, startDate),
				query.CreateFilter(`dsr."Entity_Id"`, query.OpNotNull, nil),
			}},
		},
		Sort:  []query.SortField{query.CreateAscSort(`dsr."Created_at"`)},
		Limit: 1,
	}

	err = qbSatuData.ExecuteQueryRow(ctx, satudataDB, qSatuData, &baseData)
	if err != nil {
		return nil, err // Akan melempar sql.ErrNoRows jika tidak ada data baru
	}

	// Map data dari `satudata` ke struct utama
	// SourceID (UUID) digunakan untuk tracking, sedangkan ID (int64) adalah dari tabel log,
	// yang akan diisi saat menyimpan log.
	data.SourceID = baseData.ID
	data.CreatedAt = baseData.CreatedAt
	data.ID = 0 // ID dari tabel log akan diisi nanti, bukan dari query ini.
	data.ServiceRequestID = baseData.ServiceRequestID
	data.EncounterIHS = baseData.EncounterIHS
	data.PatientIHS = baseData.PatientIHS
	data.NoMR = baseData.NoMR
	data.PractitionerIHS = baseData.PractitionerIHS
	data.StartedDate = baseData.StartedDate

	// --- Langkah 2: Ambil Accession Number dan Modality dari `ris` (MySQL) ---
	if baseData.NoRegister.Valid {
		qbRis := query.NewSQLQueryBuilder(query.DBTypeMySQL).SetSecurityOptions(false, 0)
		// PERBAIKAN: Secara eksplisit mengizinkan kolom yang akan digunakan dalam filter query RIS.
		// Ini untuk mengatasi masalah validasi pada query builder yang menganggap kolom 'noregister' tidak valid.
		qbRis.SetAllowedColumns([]string{
			"noregister", "DATE(daftar)",
		})

		risFilters := []query.DynamicFilter{
			query.CreateFilter("noregister", query.OpEqual, baseData.NoRegister.String),
		}
		// Filter berdasarkan tanggal order jika ada, untuk mempersempit pencarian
		if baseData.TanggalOrder.Valid {
			risFilters = append(risFilters, query.CreateFilter("DATE(daftar)", query.OpEqual, baseData.TanggalOrder.Time.Format("2006-01-02")))
		}

		qRis := query.DynamicQuery{
			From: "periksa",
			Fields: []query.SelectField{
				{Expression: "nofoto", Alias: "accession_number"},
				{Expression: "modality", Alias: "modality"},
			},
			Filters: []query.FilterGroup{
				{Filters: risFilters},
			},
			Limit: 1,
		}

		// Mengeluarkan Log hasil SQL query yang di-generate
		sqlStr, args, errBuild := qbRis.BuildQuery(qRis)
		if errBuild == nil {
			logger.Default().Debug("[IMAGING STUDY REPO] RIS Query Generated", logger.String("query", sqlStr), logger.Any("args", args))
		} else {
			logger.Default().Error("[IMAGING STUDY REPO] Failed to build RIS Query", logger.ErrorField(errBuild))
		}

		// PERBAIKAN: Jangan abaikan semua error. Log error jika bukan 'sql.ErrNoRows'.
		// Ini penting untuk mendeteksi masalah koneksi atau query ke database RIS.
		if errRis := qbRis.ExecuteQueryRow(ctx, risDB, qRis, &data); errRis != nil && errRis != sql.ErrNoRows {
			logger.Default().Warn("[IMAGING STUDY REPO] Gagal mengambil data dari RIS, melanjutkan tanpa data RIS.",
				logger.String("query", sqlStr), logger.Any("args", args), logger.ErrorField(errRis))
		}
	}

	return &data, nil
}

func (r *repository) SaveSatuSehatID(ctx context.Context, sourceID string, fhirID string) error {
	// Perhatian: Karena ID dari data_service_requests adalah UUID,
	// kita tidak bisa lagi menggunakan `id` (int64) dari tabel log untuk melakukan UPDATE.
	// Fungsi ini perlu penyesuaian lebih lanjut jika Anda perlu menyimpan fhir_id kembali ke tabel `data_service_requests`.
	// Untuk sementara, fungsi ini tidak akan melakukan apa-apa untuk menghindari error.
	// return nil

	// satudataDB, err := r.dbManager.GetSQLXDB("satudata")
	// if err != nil {
	// 	return err
	// }
	// qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
	// _, err = qb.ExecuteUpdate(ctx, satudataDB, "public.data_service_requests", query.UpdateData{Columns: []string{"fhir_id"}, Values: []interface{}{fhirID}}, []query.FilterGroup{query.CreateAndFilterGroup([]query.DynamicFilter{query.CreateEqualFilter("id", sourceID)})})
	// return err
	return nil
}

func (r *repository) SaveSyncLog(ctx context.Context, logData SyncLog) error {
	satudataDB, err := r.dbManager.GetSQLXDB("satudata")
	if err != nil {
		return err
	}

	qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
	insertData := query.InsertData{
		Columns: []string{"imaging_id", "fhir_id", "request_payload", "response_payload", "status", "error_message"},
		Values:  []interface{}{logData.ImagingStudyID, logData.SatuSehatID, logData.RequestPayload, logData.ResponsePayload, logData.Status, logData.ErrorMessage},
	}
	_, err = qb.ExecuteUpsert(ctx, satudataDB, "public.log_satusehat_imagingstudy", insertData, []string{"imaging_id"}, []string{"fhir_id", "request_payload", "response_payload", "status", "error_message"})
	return err
}
