package data

import (
	"context"
	"errors"
	"fmt"
	"time"

	"git.vdhsn.com/barretthousen/barretthousen/src/auth/internal/data/postgres"
	"github.com/jackc/pgx/v4"
)

type (
	PGRunnerStorage struct {
		*postgres.Queries
	}

	RegisterAccountInput struct {
		Email        string
		PasswordHash string
		Role         string
	}
)

var (
	ErrSessionNotFound       = errors.New("session not found")
	ErrAccountNotFound       = errors.New("account not found by the provided email or id")
	ErrCouldNotCreateSession = errors.New("could not create session")
)

func (db *PGRunnerStorage) RegisterAccount(ctx context.Context, in RegisterAccountInput) (accountId int, err error) {
	var accId int32
	if accId, err = db.Queries.RegisterAccount(ctx, postgres.RegisterAccountParams{
		Email:        in.Email,
		Passwordhash: in.PasswordHash,
		Role:         in.Role,
	}); err != nil {
		err = fmt.Errorf("could not register a new account with DB: %w", err)
		return
	}

	accountId = int(accId)
	return
}

type (
	GetAccountInput struct {
		AccountID int
		Email     string
	}

	AccountResult struct {
		ID           int       `json:"id"`
		Created      time.Time `json:"created"`
		Verified     time.Time `json:"verified,omitempty"`
		Email        string    `json:"email"`
		PasswordHash string    `json:"password_hash,omitempty"`
		Role         string    `json:"role"`
		Enabled      bool      `json:"enabled"`
	}
)

func (db *PGRunnerStorage) GetAccount(ctx context.Context, in GetAccountInput) (out AccountResult, err error) {
	var row postgres.GetAccountByEmailOrIdRow
	if row, err = db.Queries.GetAccountByEmailOrId(ctx, postgres.GetAccountByEmailOrIdParams{
		Email:     in.Email,
		Accountid: int32(in.AccountID),
	}); errors.Is(err, pgx.ErrNoRows) {
		err = ErrAccountNotFound
		return
	} else if err != nil {
		err = fmt.Errorf("could not execute GetSessionAccount query: %w", err)
		return
	}

	out = AccountResult{
		ID:       int(row.ID),
		Email:    row.Email,
		Role:     row.Role,
		Created:  row.Createdts,
		Verified: row.Verifiedts.Time,
		Enabled:  true,
	}

	return
}

func (db *PGRunnerStorage) GetAccountBySession(ctx context.Context, sessionID int) (out AccountResult, err error) {
	var row postgres.GetSessionAccountRow
	if row, err = db.Queries.GetSessionAccount(ctx, int32(sessionID)); errors.Is(err, pgx.ErrNoRows) {
		err = ErrSessionNotFound
		return
	} else if err != nil {
		err = fmt.Errorf("could not execute GetSessionAccount query: %w", err)
		return
	}

	out = AccountResult{
		ID:       int(row.ID),
		Email:    row.Email,
		Role:     row.Role,
		Created:  row.Createdts,
		Verified: row.Verifiedts.Time,
		Enabled:  true,
	}

	return
}

type (
	CreateSessionInput struct {
		Email        string
		PasswordHash string
	}

	Session struct {
		ID        int
		AccountID int
		Created   time.Time
	}
)

func (db *PGRunnerStorage) CreateSession(ctx context.Context, in CreateSessionInput) (out Session, err error) {
	var session postgres.AuthSession
	if session, err = db.Queries.CreateSession(ctx, postgres.CreateSessionParams{
		Email:        in.Email,
		Passwordhash: in.PasswordHash,
	}); errors.Is(err, pgx.ErrNoRows) {
		err = ErrCouldNotCreateSession
		return
	} else if err != nil {
		err = fmt.Errorf("could not execute query: %w", err)
		return
	}

	out = Session{
		ID:        int(session.ID),
		AccountID: int(session.Accountid),
		Created:   session.Createdts,
	}

	return
}

type VerifyAccountInput struct {
	Token string
}

func (db *PGRunnerStorage) VerifyAccount(ctx context.Context, in VerifyAccountInput) (err error) {
	err = errors.New("Unimplemented")
	return
}
