package mysqlbenefactor

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) IsExistBenefactorByPhoneNumber(ctx context.Context, phoneNumber string) (bool, entity.Benefactor, error) {
	const op = "mysqlbenefactor.IsExistBenefactorByPhoneNumber"

	row := d.conn.Conn().QueryRowContext(ctx, `select * from benefactors where phone_number = ?`, phoneNumber)

	Benefactor, err := scanBenefactor(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 false, entity.Benefactor{}, nil
		}

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

	return true, Benefactor, nil
}

func (d *DB) IsExistBenefactorByID(ctx context.Context, id uint) (bool, error) {
	const op = "mysqlbenefactor.IsExistBenefactorByID"

	row := d.conn.Conn().QueryRowContext(ctx, `select * from benefactors where id = ?`, id)

	_, err := scanBenefactor(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 false, nil
		}

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

	return true, nil
}

func scanBenefactor(scanner mysql.Scanner) (entity.Benefactor, error) {
	var createdAt time.Time
	var benefactor entity.Benefactor
	var roleStr, statusStr string
	// TODO - use db model and mapper between entity and db model OR use this approach

	var benefactorNullableFields nullableFields

	err := scanner.Scan(&benefactor.ID, &benefactorNullableFields.firstName,
		&benefactorNullableFields.lastName, &benefactor.PhoneNumber,
		&benefactorNullableFields.address, &benefactorNullableFields.description,
		&benefactorNullableFields.email, &benefactorNullableFields.city, &benefactorNullableFields.genderStr,
		&statusStr, &benefactorNullableFields.birthdate, &roleStr,
		&createdAt)

	benefactor.Role = entity.MapToUserRole(roleStr)
	benefactor.Status = entity.MapToBenefactorStatus(statusStr)
	mapNotNullToBenefactor(benefactorNullableFields, &benefactor)

	return benefactor, err
}

type nullableFields struct {
	firstName   sql.NullString
	lastName    sql.NullString
	address     sql.NullString
	description sql.NullString
	email       sql.NullString
	city        sql.NullString
	genderStr   sql.NullString
	birthdate   sql.NullTime
}

// TODO - find the other solution.
func mapNotNullToBenefactor(data nullableFields, benefactor *entity.Benefactor) {
	if data.firstName.Valid {
		benefactor.FirstName = data.firstName.String
	}
	if data.lastName.Valid {
		benefactor.LastName = data.lastName.String
	}
	if data.address.Valid {
		benefactor.Address = data.address.String
	}
	if data.description.Valid {
		benefactor.Description = data.description.String
	}
	if data.email.Valid {
		benefactor.Email = data.email.String
	}
	if data.city.Valid {
		benefactor.City = data.city.String
	}
	if data.genderStr.Valid {
		benefactor.Gender = entity.MapToGender(data.genderStr.String)
	}
	if data.birthdate.Valid {
		benefactor.Birthdate = data.birthdate.Time
	}
}