package controllers

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"reflect"
	"time"

	"kemendagri/sipd/services/master-sipd/models"
	"kemendagri/sipd/services/master-sipd/utils"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

type KelurahanController struct {
	contextTimeout time.Duration
	pgxConn        *pgxpool.Pool
}

func NewKelurahanController(conn *pgxpool.Pool, timeout time.Duration) (controller *KelurahanController) {
	controller = &KelurahanController{
		pgxConn:        conn,
		contextTimeout: timeout,
	}

	return
}

func getKelurahanDetailFields() []string {
	var field []string
	v := reflect.ValueOf(models.MstKelurahanModel{})
	for i := 0; i < v.Type().NumField(); i++ {
		field = append(field, v.Type().Field(i).Tag.Get("json"))
	}

	// log.Println("fields: ", field)
	return field
}

// Index lists of kelurahan
func (c *KelurahanController) Index(
	page, limit int,
	filter string,
) (
	resp []models.MstKelurahanModel,
	totalCount int,
	pageCount int,
	err error,
) {
	var q string
	resp = make([]models.MstKelurahanModel, 0)

	pageCount = 1
	offset := limit * (page - 1)

	var filterCond string
	if filter != "" {
		var filterMap map[string]string
		filterMap, err = utils.ValidateAndReturnFilterMap(filter, getKelurahanDetailFields())
		if err != nil {
			return resp, totalCount, pageCount, err
		}
		// log.Println("filterMap: ", filterMap)

		var key, val string
		for k, v := range filterMap {
			key = k
			val = v
		}

		switch key {
		case "nama_kecamatan":
			filterCond = fmt.Sprintf(" AND kc.%s ILIKE '%s%s%s' ", key, "%", val, "%")
		case "nama_kelurahan":
			filterCond = fmt.Sprintf(" AND kl.%s ILIKE '%s%s%s' ", key, "%", val, "%")
		case "kode_kecamatan":
			filterCond = fmt.Sprintf(" AND kc.%s = '%s' ", key, val)
		case "kode_kelurahan", "id_kabkota", "id_kecamatan":
			filterCond = fmt.Sprintf(" AND kl.%s = '%s' ", key, val)
		default:
			filterCond = ""
		}
	}

	// total data
	q = `SELECT COALESCE(COUNT(kl.id_kelurahan), 0)
		FROM "master_data"."mst_kelurahan" AS kl
		LEFT JOIN "master_data"."mst_kecamatan" AS kc ON kc.id_kecamatan=kl.id_kecamatan AND kc.id_daerah=kl.id_daerah
		LEFT JOIN "master_data"."mst_daerah" AS kb ON kb.id_daerah=kl.id_daerah
		WHERE kl.deleted_by = 0 AND kl.deleted_at='0001-01-01 00:00:00'` + filterCond
	err = c.pgxConn.QueryRow(context.Background(), q).Scan(&totalCount)
	if err != nil {
		err = utils.RequestError{
			Code:    http.StatusInternalServerError,
			Message: "gagal mengambil total data. - " + err.Error(),
		}
		return resp, totalCount, pageCount, err
	}

	var rows pgx.Rows
	q = `SELECT kl.id_kelurahan, kl.id_kecamatan, kl.id_daerah as id_kabkota, kb.kode_ddn as kode_kabkota, kb.nama_daerah as nama_kabkota
				, kc.kode_kecamatan, kc.nama_kecamatan, kl.kode_kelurahan, kl.nama_kelurahan, kl.locked
		FROM "master_data"."mst_kelurahan" AS kl
		LEFT JOIN "master_data"."mst_kecamatan" AS kc ON kc.id_kecamatan=kl.id_kecamatan AND kc.id_daerah=kl.id_daerah
		LEFT JOIN "master_data"."mst_daerah" AS kb ON kb.id_daerah=kl.id_daerah
		WHERE kl.deleted_by = 0 AND kl.deleted_at='0001-01-01 00:00:00' `
	q += filterCond
	q += ` ORDER BY kl.kode_kelurahan LIMIT $1 OFFSET $2`
	rows, err = c.pgxConn.Query(context.Background(), q, limit, offset)
	if err != nil {
		err = utils.RequestError{
			Code:    http.StatusInternalServerError,
			Message: "gagal mengambil data. - " + err.Error(),
		}
		return resp, totalCount, pageCount, err
	}
	defer rows.Close()
	for rows.Next() {
		var m models.MstKelurahanModel
		err = rows.Scan(
			&m.IDKelurahan,
			&m.IDKecamatan,
			&m.IDKabkota,
			&m.KodeKabkota,
			&m.NamaKabkota,
			&m.KodeKecamatan,
			&m.NamaKecamatan,
			&m.KodeKelurahan,
			&m.NamaKelurahan,
			&m.Locked,
		)
		if err != nil {
			err = utils.RequestError{
				Code:    http.StatusInternalServerError,
				Message: "gagal iterasi data. - " + rows.Err().Error(),
			}
			return resp, totalCount, pageCount, err
		}
		resp = append(resp, m)
	}
	if rows.Err() != nil {
		err = utils.RequestError{
			Code:    http.StatusInternalServerError,
			Message: "gagal mengambil data (rows). - " + rows.Err().Error(),
		}
		return resp, totalCount, pageCount, err
	}
	rows.Close()

	if totalCount > 0 && totalCount > limit {
		pageCount = int(math.Ceil(float64(totalCount) / float64(limit)))
	}

	return resp, totalCount, pageCount, err
}

// View detail of kelurahan
func (c *KelurahanController) View(id int) (resp models.MstKelurahanDetilModel, err error) {
	// log.Println("id: ", id)
	q := `SELECT kl.id_kelurahan, kl.id_kecamatan, kl.id_daerah as id_kabkota, kb.id_prov, pv.kode_ddn as kode_prov, pv.nama_daerah as nama_prov
				, kb.kode_ddn as kode_kabkota, kb.nama_daerah as nama_kabkota
				, kc.kode_kecamatan, kc.nama_kecamatan, kl.kode_kelurahan, kl.nama_kelurahan, kl.locked
				, kl.created_at, kl.created_by, kl.updated_at, kl.updated_by, kl.deleted_at, kl.deleted_by
		FROM "master_data"."mst_kelurahan" AS kl
		LEFT JOIN "master_data"."mst_kecamatan" AS kc ON kc.id_kecamatan=kl.id_kecamatan AND kc.id_daerah=kl.id_daerah
		LEFT JOIN "master_data"."mst_daerah" AS kb ON kb.id_daerah=kl.id_daerah
		LEFT JOIN "master_data"."mst_daerah" AS pv ON pv.id_daerah=kb.id_prov
		WHERE kl.deleted_by = 0 AND kl.deleted_at='0001-01-01 00:00:00' AND kl.id_kelurahan=$1`

	err = c.pgxConn.QueryRow(context.Background(), q, id).Scan(
		&resp.IDKelurahan,
		&resp.IDKecamatan,
		&resp.IDKabkota,
		&resp.IDProv,
		&resp.KodeProv,
		&resp.NamaProv,
		&resp.KodeKabkota,
		&resp.NamaKabkota,
		&resp.KodeKecamatan,
		&resp.NamaKecamatan,
		&resp.KodeKelurahan,
		&resp.NamaKelurahan,
		&resp.Locked,
		&resp.CreatedAt,
		&resp.CreatedBy,
		&resp.UpdatedAt,
		&resp.UpdatedBy,
		&resp.DeletedAt,
		&resp.DeletedBy,
	)
	if err != nil {
		if err.Error() == "no rows in result set" {
			err = utils.RequestError{
				Code:    http.StatusNotFound,
				Message: "Data tidak ditemukan",
			}
		} else {
			err = utils.RequestError{
				Code:    http.StatusInternalServerError,
				Message: "gagal mengambil data. - " + err.Error(),
			}
		}

		return resp, err
	}

	if resp.DeletedBy > 0 && !resp.DeletedAt.IsZero() {
		err = utils.RequestError{
			Code:    http.StatusNotFound,
			Message: "Data sudah dihapus.",
		}
		return resp, err
	}

	return resp, err
}

// Create new daerah
// func (c *DaerahController) Create(payloadl models.RefDaerahModelPayload) error {
// 	var err error

// 	var kodeUnik string
// 	kodeUnik = uuid.New().String()

// 	// mulai validasi duplikat unique key
// 	var exists bool
// 	q := `
//     SELECT EXISTS (
//         SELECT 1
//         FROM "master_data"."mst_daerah"
//         WHERE kode_ddn = $1
//           AND nama_daerah = $2
//     )
//     `
// 	err = c.pgxConn.QueryRow(
// 		context.Background(),
// 		q,
// 		payloadl.KodeDdn,
// 		payloadl.NamaDaerah,
// 	).Scan(&exists)
// 	if err != nil {
// 		err = utils.RequestError{
// 			Code:    http.StatusInternalServerError,
// 			Message: "gagal memeriksa duplikasi unique key. - " + err.Error(),
// 		}
// 		return err
// 	}
// 	if exists {
// 		err = utils.RequestError{
// 			Code:    http.StatusBadRequest,
// 			Message: "Data sudah ada",
// 		}
// 		return err
// 	}
// 	// selesai validasi duplikat unique key

// 	q = `
// 		INSERT INTO "master_data"."mst_daerah" (
// 			id_prov,
// 			daerah_khusus,
// 			jns_pemda,
// 			zona_waktu,
// 			zona_wilayah,
// 			kode_unik,
// 			kode_ddn,
// 			nama_daerah,
// 			nama_ibu_kota,
// 			logo_daerah
// 		) VALUES (
// 			$1,	 -- id
// 			$2,  -- id_prov
// 			$3,  -- daerah_khusus
// 			$4,  -- jns_pemda
// 			$5,  -- zona_waktu
// 			$6,  -- zona_wilayah
// 			$7,  -- kode_unik
// 			$8,  -- kode_ddn
// 			$9,  -- nama_daerah
// 			$10 -- nama_ibu_kota
// 		)
// 	`
// 	_, err = c.pgxConn.Exec(
// 		context.Background(),
// 		q,
// 		&payloadl.IDProv,
// 		&payloadl.DaerahKhusus,
// 		&payloadl.JnsPemda,
// 		&payloadl.ZonaWaktu,
// 		&payloadl.ZonaWilayah,
// 		&kodeUnik,
// 		&payloadl.KodeDdn,
// 		&payloadl.NamaDaerah,
// 		&payloadl.NamaIbuKota,
// 		&payloadl.LogoDaerah,
// 	)
// 	if err != nil {
// 		err = utils.RequestError{
// 			Code:    http.StatusInternalServerError,
// 			Message: "gagal menyimpan data daerah. - " + err.Error(),
// 		}
// 		return err
// 	}

// 	return err
// }

// // Update daerah
// func (c *DaerahController) Update(id int, payloadl models.RefDaerahModelPayload) error {
// 	var err error
// 	var q string

// 	// pastikan data dengan id tersebut ada, dengan cara mengambil datanya.
// 	var idDaerah int
// 	var kodeDdn, namaDaerah string
// 	q = `SELECT id, kode_ddn, nama_daerah FROM "master_data"."mst_daerah" WHERE id_daerah=$1`
// 	err = c.pgxConn.QueryRow(context.Background(), q, id).Scan(&idDaerah, &kodeDdn, &namaDaerah)
// 	if err != nil {
// 		{
// 			err = utils.RequestError{
// 				Code:    http.StatusInternalServerError,
// 				Message: "gagal mengambil data daerah. - " + err.Error(),
// 			}
// 			return err
// 		}
// 	}

// 	if kodeDdn != payloadl.KodeDdn && namaDaerah != payloadl.NamaDaerah {
// 		// mulai validasi duplikat unique key
// 		var exists bool
// 		q = `
// 				SELECT EXISTS (
// 					SELECT 1
// 					FROM "master_data"."mst_daerah"
// 					WHERE kode_ddn = $1
// 					AND nama_daerah = $2
// 				)
//     		`
// 		err = c.pgxConn.QueryRow(
// 			context.Background(),
// 			q,
// 			payloadl.KodeDdn,
// 			payloadl.NamaDaerah,
// 		).Scan(&exists)
// 		if err != nil {
// 			err = utils.RequestError{
// 				Code:    http.StatusInternalServerError,
// 				Message: "gagal memeriksa duplikasi unique key. - " + err.Error(),
// 			}
// 			return err
// 		}
// 		if exists {
// 			err = utils.RequestError{
// 				Code:    http.StatusBadRequest,
// 				Message: "Data sudah ada",
// 			}
// 			return err
// 		}
// 		// selesai validasi duplikat unique key
// 	}

// 	q = `
// 		UPDATE "master_data"."mst_daerah"
// 		SET
// 			id_prov = $1,
// 			daerah_khusus = $2,
// 			jns_pemda = $3,
// 			zona_waktu = $4,
// 			zona_wilayah = $5,
// 			nama_daerah = $6,
// 			nama_ibu_kota = $7,
// 			logo_daerah = $8,
// 			updated_at = NOW()
// 		WHERE id = $9
// 		`
// 	_, err = c.pgxConn.Exec(
// 		context.Background(),
// 		q,
// 		payloadl.IDProv,
// 		payloadl.DaerahKhusus,
// 		payloadl.JnsPemda,
// 		payloadl.ZonaWaktu,
// 		payloadl.ZonaWilayah,
// 		payloadl.NamaDaerah,
// 		payloadl.NamaIbuKota,
// 		payloadl.LogoDaerah,
// 		id, // kondisi WHERE
// 	)
// 	if err != nil {
// 		err = utils.RequestError{
// 			Code:    http.StatusInternalServerError,
// 			Message: "gagal mengupdate data daerah. - " + err.Error(),
// 		}
// 		return err
// 	}

// 	return err
// }

// func (c *DaerahController) Delete(id int) error {
// 	var err error
// 	var idDaerah, deletedBy int
// 	var deletedAt time.Time

// 	q := `SELECT id, deleted_by, deleted_at FROM "master_data"."mst_daerah" WHERE id_daerah=$1`
// 	err = c.pgxConn.QueryRow(context.Background(), q, id).Scan(&idDaerah, &deletedBy, &deletedAt)
// 	if err != nil {
// 		{
// 			err = utils.RequestError{
// 				Code:    http.StatusInternalServerError,
// 				Message: "gagal mengambil data daerah. - " + err.Error(),
// 			}
// 			return err
// 		}
// 	}

// 	if deletedBy != 0 {
// 		err = utils.RequestError{
// 			Code:    http.StatusBadRequest,
// 			Message: "data sudah dihapus",
// 		}
// 		return err
// 	}

// 	q = `
// 		UPDATE "master_data"."mst_daerah"
// 		SET
// 			deleted_by = $1,
// 			deleted_at = NOW()
// 		WHERE id = $2
// 		`
// 	_, err = c.pgxConn.Exec(
// 		context.Background(),
// 		q,
// 		id,
// 		id,
// 	)
// 	if err != nil {
// 		err = utils.RequestError{
// 			Code:    http.StatusInternalServerError,
// 			Message: "gagal menghapus data daerah. - " + err.Error(),
// 		}
// 		return err
// 	}

// 	return err
// }
