package account

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

	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"

	"github.com/lib/pq"
)

type Database struct {
	*postgresql.DatabasePostgreSQL
}

func (db *Database) GetAllAccountOfUser(id string) ([]*Account, error) {
	q := `
		SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at
		FROM public.account a
		WHERE a.user_id = $1
	`
	rows, err := db.Session.Query(q, id)
	if err != nil {
		return nil, err
	}
	defer func(rows *sql.Rows) {
		err := rows.Close()
		if err != nil {
			return
		}
	}(rows)

	as := make([]*Account, 0)
	for rows.Next() {
		a := Account{}
		err := rows.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
		if err != nil {
			return nil, err
		}
		as = append(as, &a)
	}
	return as, nil
}

func (db *Database) GetAccountWithNameForUser(name string, id string) (*Account, error) {
	q := `
		SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at
		FROM public.account a
		WHERE a.user_id = $1
		AND a.name = $2
	`
	row, err := db.Session.Query(q, id, name)
	if err != nil {
		return nil, err
	}
	if !row.Next() {
		return nil, dao.NewDAOError(dao.ErrTypeNotFound, fmt.Errorf("no row found"))
	}
	a := Account{}
	err = row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
	if err != nil {
		return nil, err
	}
	if row.Next() {
		return nil, fmt.Errorf("Impossibru")
	}
	var errPq *pq.Error
	if errors.As(err, &errPq) {
		return nil, postgresql.HandlePgError(errPq)
	}
	if err != nil {
		utils.GetLogger().Info(err)
		return nil, err
	}
	return &a, nil
}

func (db *Database) CreateAccount(account *Account) error {
	q := `
		INSERT INTO public.account
			(Name, Provider, user_id)
		VALUES
			($1, $2, $3)
		RETURNING id, created_at
	`

	err := db.Session.
		QueryRow(q, account.Name, account.Provider, account.UserId).
		Scan(&account.ID, &account.CreatedAt)
	if errPq, ok := err.(*pq.Error); ok {
		return postgresql.HandlePgError(errPq)
	}
	return err

}

func (db *Database) DeleteAccountOfAnUser(userId, accountId string) error {
	query := `
		DELETE FROM account
		WHERE user_id = $1
		AND id = $2;`
	_, err := db.Session.Exec(query, userId, accountId)
	return err
}

func (db *Database) GetASpecificAccountForUser(userId, accountId string) (*Account, error) {
	q := `
		SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at
		FROM public.account a
		WHERE a.user_id = $1
		AND a.id = $2
	`
	row := db.Session.QueryRow(q, userId, accountId)
	a := Account{}
	err := row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
	if errPq, ok := err.(*pq.Error); ok {
		return nil, postgresql.HandlePgError(errPq)
	}
	if err != nil {
		utils.GetLogger().Info(err)
		return nil, err
	}
	return &a, nil
}

func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database {
	return &Database{db}
}
