package user

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

	"git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model"
)

type UserDB interface {
	AddUser(ctx context.Context, user *model.User) error
	GetUserBySubject(ctx context.Context, subject string) (*model.User, error)
}

var ErrNotFound = errors.New("not found")

const getUserQuery = `
	SELECT id, name, family_name, given_name, nickname, picture, updated_at, email, email_verified
	FROM user
	WHERE id = ?
`
const insertUserQuery = `
	INSERT INTO user (id, name, family_name, given_name, nickname, picture, updated_at, email, email_verified)
	VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
`

type sqlUserDB struct {
	db *sql.DB
}

func (db *sqlUserDB) GetUserBySubject(ctx context.Context, subject string) (*model.User, error) {
	row := db.db.QueryRowContext(ctx, getUserQuery, subject)
	var res model.User
	if err := row.Scan(&res.Subject, &res.Name, &res.FamilyName, &res.GivenName, &res.Nickname, &res.Picture, &res.UpdatedAt, &res.Email, &res.EmailVerified); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, ErrNotFound
		}
		return nil, fmt.Errorf("failed to read result from DB: %w", err)
	}
	return &res, nil
}

func (db *sqlUserDB) AddUser(ctx context.Context, user *model.User) error {
	tx, err := db.db.BeginTx(ctx, nil)
	if err != nil {
		return fmt.Errorf("failed to start transaction: %w", err)
	}
	defer func() { _ = tx.Rollback() }()

	if _, err := tx.ExecContext(ctx, insertUserQuery, user.Subject, user.Name, user.FamilyName, user.GivenName, user.Nickname, user.Picture, user.UpdatedAt, user.Email, user.EmailVerified); err != nil {
		return fmt.Errorf("failed to insert in DB: %w", err)
	}
	if err := tx.Commit(); err != nil {
		return fmt.Errorf("failed to commit transaction: %w", err)
	}
	return nil
}

func New(db *sql.DB) *sqlUserDB {
	return &sqlUserDB{db: db}
}
