package pgdb

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	dberrors "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/errors"
	dbtypes "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/types"
	"github.com/Masterminds/squirrel"
	"github.com/google/uuid"
)

func (c *client) GetClientValidation(
	ctx context.Context,
	request *dbtypes.ClientValidationGetRequest,
) (*dbtypes.ClientValidationGetResponse, error) {
	if request == nil {
		return nil, fmt.Errorf("%w: request is nil", dberrors.ErrBadRequest)
	}

	var (
		psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)

		userValidationTable = fmt.Sprintf("%s.%s", c.config.Schema, UserValidationTableName)
	)

	getUserValidation := psql.Select(
		"status", "description",
	).
		From(userValidationTable).
		Where(squirrel.Eq{"uid": request.UserId})

	query, args, err := getUserValidation.ToSql()
	if err != nil {
		return nil, fmt.Errorf("%w: error building 'get user validation' query: %v", dberrors.ErrInternal, err)
	}

	row := c.db.QueryRowContext(ctx, query, args...)

	var result dbtypes.ClientValidation

	if err := row.Scan(&result.Status, &result.Description); err != nil {
		if err == sql.ErrNoRows {
			return nil, dberrors.ErrNotFound
		}

		return nil, fmt.Errorf("%w: error scanning row for 'get user validation' query: %v", dberrors.ErrInternal, err)
	}

	return &dbtypes.ClientValidationGetResponse{
		ClientValidation: &result,
	}, nil
}

func (c *client) CreateUser(
	ctx context.Context,
	request *dbtypes.UserSaveRequest,
) (*dbtypes.UserSaveResponse, error) {
	if request == nil {
		return nil, fmt.Errorf("%w: request is nil", dberrors.ErrBadRequest)
	}

	tx, err := c.db.BeginTx(ctx, nil)
	if err != nil {
		return nil, fmt.Errorf("%w: error starting transaction: %v", dberrors.ErrInternal, err)
	}

	defer func() { _ = tx.Rollback() }()

	result, err := c.createUser(ctx, tx, request)
	if err != nil {
		return nil, fmt.Errorf("error creating user: %w", err)
	}

	if err := c.createUserValidationTicket(ctx, tx, request); err != nil {
		return nil, fmt.Errorf("error creating user validation ticket: %w", err)
	}

	resp, err := c.createCompany(ctx, tx, &dbtypes.CompanyCreateRequest{
		OwnerId: request.Id,
		Staff: []string{
			request.Id,
		},
	})
	if err != nil {
		return nil, fmt.Errorf("error creating company: %w", err)
	}

	var ownerId string

	switch request.Type {
	case dbtypes.UserTypeAgent:
		ownerId = request.Id
	case dbtypes.UserTypeDistributor:
		ownerId = resp.Id
	default:
		return nil, fmt.Errorf("%w: unknown user type: %v", dberrors.ErrBadRequest, request.Type)
	}

	balanceId := fmt.Sprintf("%sBAL", strings.ReplaceAll(uuid.NewString(), "-", ""))

	if err := c.createBalance(ctx, tx, &dbtypes.BalanceCreateRequest{
		Id:           balanceId,
		OwnerId:      ownerId,
		RawBalance:   0,
		CleanBalance: 0,
	}); err != nil {
		return nil, fmt.Errorf("error creating balance: %w", err)
	}

	if err := tx.Commit(); err != nil {
		return nil, fmt.Errorf("%w: error committing transaction: %w", dberrors.ErrInternal, err)
	}

	return result, nil
}

func (c *client) createUser(
	ctx context.Context,
	driver Driver,
	request *dbtypes.UserSaveRequest,
) (*dbtypes.UserSaveResponse, error) {
	var (
		psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)

		userTable = fmt.Sprintf("%s.%s", c.config.Schema, UsersTableName)
	)

	saveUser := psql.Insert(userTable).
		Columns(
			"uid", "name", "phone", "email", "client_type_id",
		).
		Values(
			request.Id, request.FullName, request.Phone, request.Email, request.Type,
		)

	query, args, err := saveUser.ToSql()
	if err != nil {
		return nil, fmt.Errorf("%w: error building 'save user' query: %v", dberrors.ErrInternal, err)
	}

	res, err := driver.ExecContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("%w: error executing 'save user' query: %v", dberrors.ErrInternal, err)
	}

	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return nil, fmt.Errorf("%w: error getting rows affected for 'save user' query: %v", dberrors.ErrInternal, err)
	}

	if rowsAffected == 0 {
		return nil, dberrors.ErrInternal
	}

	return &dbtypes.UserSaveResponse{}, nil
}

func (c *client) createUserValidationTicket(
	ctx context.Context,
	driver Driver,
	request *dbtypes.UserSaveRequest,
) error {
	var (
		psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)

		userValidationTable = fmt.Sprintf("%s.%s", c.config.Schema, UserValidationTableName)
	)

	// TODO: use normal uuid after DB reengineering
	ticketId := fmt.Sprintf("%sVAL", strings.ReplaceAll(uuid.NewString(), "-", ""))

	createUserValidation := psql.Insert(userValidationTable).
		Columns(
			"id", "uid", "status", "description", "last_update",
		).
		Values(
			ticketId, request.Id, dbtypes.ClientValStatusNew, request.FullName, squirrel.Expr("CURRENT_TIMESTAMP"),
		)

	query, args, err := createUserValidation.ToSql()
	if err != nil {
		return fmt.Errorf("%w: error building 'create user validation' query: %v", dberrors.ErrInternal, err)
	}

	res, err := driver.ExecContext(ctx, query, args...)
	if err != nil {
		return fmt.Errorf("%w: error executing 'create user validation' query: %v", dberrors.ErrInternal, err)
	}

	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return fmt.Errorf("%w: error getting rows affected for 'create user validation' query: %v", dberrors.ErrInternal, err)
	}

	if rowsAffected == 0 {
		return dberrors.ErrInternal
	}

	return nil
}
