package mysqladmin

import (
	"context"
	"database/sql"
	"errors"
	"time"

	"git.gocasts.ir/ebhomengo/niki/entity"
	errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
	richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
	"git.gocasts.ir/ebhomengo/niki/repository/mysql"
)

func (d *DB) GetAdminByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Admin, error) {
	const op = "mysqladmin.GetAdminByPhoneNumber"

	query := `select * from admins where phone_number = ?`
	//nolint
	stmt, err := d.conn.PrepareStatement(ctx, mysql.StatementKeyAdminGetByPhoneNumber, query)
	if err != nil {
		return entity.Admin{}, richerror.New(op).WithErr(err).
			WithMessage(errmsg.ErrorMsgCantPrepareStatement).WithKind(richerror.KindUnexpected)
	}

	row := stmt.QueryRowContext(ctx, phoneNumber)
	admin, err := scanAdmin(row)
	if err != nil {
		sErr := sql.ErrNoRows
		//TODO-errorsas: second argument to errors.As should not be *error
		//nolint
		if errors.As(err, &sErr) {
			return entity.Admin{}, richerror.New(op).WithErr(sErr).
				WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindNotFound)
		}

		// TODO - log unexpected error for better observability
		return entity.Admin{}, richerror.New(op).WithErr(err).
			WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
	}

	return admin, nil
}

func scanAdmin(scanner mysql.Scanner) (entity.Admin, error) {
	var createdAt, updatedAt time.Time
	var admin entity.Admin
	var roleStr, statusStr, password string
	// TODO - use db model and mapper between entity and db model OR use this approach

	var adminNullableFields nullableFields

	err := scanner.Scan(&admin.ID, &adminNullableFields.firstName,
		&adminNullableFields.lastName, &password, &admin.PhoneNumber,
		&roleStr, &adminNullableFields.description,
		&adminNullableFields.email, &adminNullableFields.genderStr,
		&statusStr, &createdAt, &updatedAt)

	admin.Role = entity.MapToAdminRole(roleStr)
	admin.Status = entity.AdminStatus(statusStr)
	admin.Password = password
	mapNotNullToAdmin(adminNullableFields, &admin)

	return admin, err
}

type nullableFields struct {
	firstName   sql.NullString
	lastName    sql.NullString
	description sql.NullString
	email       sql.NullString
	genderStr   sql.NullString
}

// TODO - find the other solution.
func mapNotNullToAdmin(data nullableFields, admin *entity.Admin) {
	if data.firstName.Valid {
		admin.FirstName = data.firstName.String
	}
	if data.lastName.Valid {
		admin.LastName = data.lastName.String
	}
	if data.description.Valid {
		admin.Description = data.description.String
	}
	if data.email.Valid {
		admin.Email = data.email.String
	}
	if data.genderStr.Valid {
		admin.Gender = entity.Gender(data.genderStr.String)
	}
}