package users

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

	sq "github.com/Masterminds/squirrel"
	"github.com/emochka2007/block-accounting/internal/pkg/models"
	sqltools "github.com/emochka2007/block-accounting/internal/pkg/sqlutils"
	"github.com/google/uuid"
)

type GetParams struct {
	Ids            uuid.UUIDs
	PKs            [][]byte
	OrganizationId uuid.UUID
	Seed           []byte
}

// todo implement
type Repository interface {
	Get(ctx context.Context, params GetParams) ([]*models.User, error)
	Create(ctx context.Context, user *models.User) error
	Activate(ctx context.Context, id uuid.UUID) error
	Update(ctx context.Context, user *models.User) error
	Delete(ctx context.Context, id string) error
}

type repositorySQL struct {
	db *sql.DB
}

func NewRepository(db *sql.DB) Repository {
	return &repositorySQL{
		db: db,
	}
}

func (s *repositorySQL) Conn(ctx context.Context) sqltools.DBTX {
	if tx, ok := ctx.Value(sqltools.TxCtxKey).(*sql.Tx); ok {
		return tx
	}

	return s.db
}

func (r *repositorySQL) Get(ctx context.Context, params GetParams) ([]*models.User, error) {
	var users []*models.User = make([]*models.User, 0, len(params.Ids))

	if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) (err error) {
		query := sq.Select("u.id, u.name, u.email, u.phone, u.tg, u.seed, u.created_at, u.activated_at, u.public_key, u.mnemonic").
			From("users as u").
			PlaceholderFormat(sq.Dollar)

		if len(params.Ids) > 0 {
			query = query.Where(sq.Eq{
				"u.id": params.Ids,
			})
		}

		if len(params.PKs) > 0 {
			query = query.Where(sq.Eq{
				"u.public_key": params.PKs,
			})
		}

		if params.OrganizationId != uuid.Nil {
			query = query.InnerJoin(
				"organizations_users as ou on ou.user_id = u.id",
			).Where(sq.Eq{
				"ou.organization_id": params.OrganizationId,
			})
		}

		if params.Seed != nil {
			query = query.Where("u.seed = ?", params.Seed)
		}

		if params.PKs != nil {
			fmt.Println(query.ToSql())
		}

		rows, err := query.RunWith(r.Conn(ctx)).QueryContext(ctx)
		if err != nil {
			return fmt.Errorf("error fetch data from database. %w", err)
		}

		defer func() {
			if cErr := rows.Close(); cErr != nil {
				err = errors.Join(fmt.Errorf("error close database rows. %w", cErr), err)
			}
		}()

		for rows.Next() {
			var (
				id uuid.UUID

				name  string
				email string
				phone string
				tg    string

				seed []byte
				pk   []byte
				//isAdmin     bool
				createdAt   time.Time
				activatedAt sql.NullTime
				mnemonic    string
			)

			if err = rows.Scan(
				&id,
				&name,
				&email,
				&phone,
				&tg,
				&seed,
				&createdAt,
				&activatedAt,
				&pk,
				&mnemonic,
			); err != nil {
				return fmt.Errorf("error scan row. %w", err)
			}

			users = append(users, &models.User{
				ID:   id,
				Name: name,
				Credentails: &models.UserCredentials{
					Email:    email,
					Phone:    phone,
					Telegram: tg,
				},
				Bip39Seed: seed,
				PK:        pk,
				Mnemonic:  mnemonic,
				//Admin:     isAdmin,
				CreatedAt: createdAt,
				Activated: activatedAt.Valid,
			})
		}

		return nil
	}); err != nil {
		return nil, err
	}

	return users, nil
}

func (r *repositorySQL) Create(ctx context.Context, user *models.User) error {
	if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) error {
		columns := []string{"id", "name", "email", "phone", "tg", "seed", "public_key", "mnemonic", "created_at"}

		values := []any{
			user.ID,
			user.Name,
			user.Credentails.Email,
			user.Credentails.Phone,
			user.Credentails.Telegram,
			user.Bip39Seed,
			user.PK,
			user.Mnemonic,
			user.CreatedAt,
		}

		if user.Activated {
			columns = append(columns, "activated_at")
			values = append(values, user.CreatedAt)
		}

		query := sq.Insert("users").Columns(
			columns...,
		).Values(
			values...,
		).PlaceholderFormat(sq.Dollar)

		if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil {
			return fmt.Errorf("error insert new user. %w", err)
		}

		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (r *repositorySQL) Activate(ctx context.Context, id uuid.UUID) error {
	if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) error {
		query := sq.Update("users").
			SetMap(sq.Eq{
				"activated_at": time.Now(),
			}).
			Where(sq.Eq{
				"id": id,
			})

		if _, err := query.RunWith(r.Conn(ctx)).ExecContext(ctx); err != nil {
			return fmt.Errorf("error mark user as activated in database. %w", err)
		}

		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (r *repositorySQL) Update(ctx context.Context, user *models.User) error {
	if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) error {

		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (r *repositorySQL) Delete(ctx context.Context, id string) error {
	if err := sqltools.Transaction(ctx, r.db, func(ctx context.Context) error {

		return nil
	}); err != nil {
		return err
	}

	return nil
}
