package staff

import (
	"database/sql"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"log"

	"com.sismedika.com.absensi/graph/model"
	"com.sismedika.com.absensi/pkg/config"
	db "com.sismedika.com.absensi/pkg/database"
	"com.sismedika.com.absensi/pkg/jwt"
)

// diambil dari graph/model/struct nya Staff
type Staff model.Staff

// staff *Staff = receiver yang memiliki tipe data Staff. liat di graphql
// email string, id_google_sign_in string = parameter inputan dari flutter nanti
// *model.Staff, error = output yang dihasilkan, jika tidak error return *model.Staff namun jika error maka sebelah kanan nya yaitu
// error

func (staff *Staff) LoginAttendance(email string, id_google_sign_in string) (*model.Staff, error) {

	// inisialisasi
	var err error
	var ret model.Staff
	var var_staff_is_login string

	// decode private.pem
	pemData, err := ioutil.ReadFile(config.Data.Get("privatekey"))
	if err != nil {
		log.Printf("read key file: %s", err)
		return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
	}

	block, _ := pem.Decode(pemData)
	if block == nil {
		log.Printf("bad key data: %s", "not PEM-encoded")
		return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
	}

	if got, want := block.Type, "RSA PRIVATE KEY"; got != want {
		log.Printf("unknown key type %q, want %q", got, want)
		return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
	}

	// generate token
	// token yg kiri merupakan output
	// _ merupakan value yg tidak digunakan dalam hal ini expired bisa di cek di fungsi jwt.GenerateToken
	// err merupakan kondisi ketika error return nya apa

	token, _, err := jwt.GenerateToken(email, id_google_sign_in)
	if err != nil {
		return nil, err
	}

	// check staff is login
	qCheck := `SELECT 
	M_StaffIsLogin
	FROM m_staff
	WHERE 
	M_StaffIsActive = 'Y'
	AND M_StaffEmail = ?
	`

	rowCheck := db.Handle.QueryRow(
		qCheck,
		email,
	)

	db.LogSQL(qCheck)
	err = rowCheck.Scan(
		&var_staff_is_login,
	)

	if err != nil {
		log.Printf("Error m_staff select isLogin: %v", err)
		log.Printf("Executing query: %s\n", qCheck)
		return nil, err
	}

	if var_staff_is_login == "N" {
		// generate token jk isLogin N

		// jika sukses generate token maka update
		// M_StaffToken & M_StaffIsLogin

		qx := `UPDATE m_staff 
		SET 
		M_StaffToken = ?,
		M_StaffIDGoogleSignIn = ?,
		M_StaffIsLogin = 'Y'
		WHERE
		M_StaffIsActive = 'Y' 
		AND M_StaffEmail = ?
	`
		// log sql untuk mengetahui query yg akan dieksekusi
		db.LogSQL(qx)

		// cek kondisi
		// _ merupakan kondisi true namun value tidak digunakan sbg cek
		// error kondisi false dan error digunakan
		_, err = db.Handle.Exec(
			qx,
			token,
			id_google_sign_in,
			email)

		if err != nil {
			return &ret, fmt.Errorf("UPDATE_TOKEN_FAILED")
		}
	}

	// data yang akan di tampilkan
	q := `SELECT
	M_StaffID,
	M_StaffNIP,
	M_StaffName,
	M_StaffEmail,
	M_StaffNoHp,
	M_CompanyID,
	M_CompanyName,
	M_StaffIsActive,
	M_StaffIsLogin,
	M_StaffToken,
	M_StaffIDGoogleSignIn,
	M_StaffCreated,
	M_StaffLastUpdated
	FROM m_staff 
	join m_company
	ON M_StaffM_CompanyID = M_CompanyID
	AND M_CompanyIsActive = 'Y'
	AND M_StaffIsActive = 'Y'
	AND M_StaffIDGoogleSignIn = ?
	AND M_StaffEmail = ?`

	// row merupakan penerima yang akan dimasukkan langsung, menggunakan anotasi :=
	// id_google_sign_in, email merupakan binding parameter dari tanda tanya "?" di sql
	row := db.Handle.QueryRow(q, id_google_sign_in, email)
	db.LogSQL(q)

	// row.Scan untuk mendapatkan field dari sql diatas
	// NOTE POSISI ROW SCAN HARUS SAMA PERSIS SEPERTI FIELD YANG AKAN DI RETURN
	err = row.Scan(
		&ret.StaffID,
		&ret.Nip,
		&ret.Name,
		&ret.Email,
		&ret.PhoneNumber,
		&ret.CompanyID,
		&ret.CompanyName,
		&ret.IsActive,
		&ret.IsLogin,
		&ret.Token,
		&ret.IDGoogleSignIn,
		&ret.CreatedAt,
		&ret.LastUpdatedAt,
	)

	// check data

	if err != nil {
		log.Printf("Error m_staff select: %v", err)
		log.Printf("Executing query: %s\n", q)
		return nil, err
	}

	if err != nil {
		if err == sql.ErrNoRows {
			return nil, fmt.Errorf("RECORD_STAFF_LOGIN_ATTENDANCE_NOT_FOUND")
		}
		return nil, err
	}

	// hasil return nya
	return &ret, err
}

// logout
func (staff *Staff) LogoutAttendance(email string, id_google_sign_in string) (*model.LogoutResponse, error) {

	// inisialisasi
	var err error
	var ret model.LogoutResponse
	var varCheckToken string

	ret.Status = new(string)
	ret.Message = new(string)
	*ret.Status = "OK"
	*ret.Message = "Logout Berhasil"

	q := `SELECT
	M_StaffToken
	FROM m_staff 
	join m_company
	ON M_StaffM_CompanyID = M_CompanyID
	AND M_CompanyIsActive = 'Y'
	AND M_StaffIsActive = 'Y'
	AND M_StaffIsLogin = 'Y'
	AND M_StaffIDGoogleSignIn = ?
	AND M_StaffEmail = ?`

	// row merupakan penerima yang akan dimasukkan langsung, menggunakan anotasi :=
	// id_google_sign_in, email merupakan binding parameter dari tanda tanya "?" di sql
	row := db.Handle.QueryRow(q, id_google_sign_in, email)
	db.LogSQL(q)

	// row.Scan untuk mendapatkan field dari sql diatas
	// NOTE POSISI ROW SCAN HARUS SAMA PERSIS SEPERTI FIELD YANG AKAN DI RETURN
	err = row.Scan(
		&varCheckToken,
	)

	// check data

	if err != nil {
		log.Printf("Error m_staff select: %v", err)
		log.Printf("Executing query: %s\n", q)
		*ret.Status = "ERR"
		*ret.Message = "Error : Check Data Failed"
		return &ret, err
	}

	qx := `UPDATE m_staff 
	SET 
	M_StaffToken = NULL,
	M_StaffIsLogin = 'N'
	WHERE
	M_StaffIsActive = 'Y' 
	AND M_StaffEmail = ?
	`
	// log sql untuk mengetahui query yg akan dieksekusi
	db.LogSQL(qx)

	// cek kondisi
	// _ merupakan kondisi true namun value tidak digunakan sbg cek
	// error kondisi false dan error digunakan
	_, err = db.Handle.Exec(
		qx,
		email)

	if err != nil {
		*ret.Status = "ERR"
		*ret.Message = "Error : UPDATE TOKEN FAILED"
		return &ret, fmt.Errorf("UPDATE_TOKEN_FAILED")
	}

	// hasil return nya
	return &ret, err
}
