package project

import (
	"context"
	"errors"
	"fmt"
	"log"
	"madsky.ru/go-finance/internal/database"
	"madsky.ru/go-finance/internal/project"
	"strings"
)

type Repository interface {
	Find(ctx context.Context) ([]*project.Project, error)
	FindOne(ctx context.Context, id uint64) (*project.Project, error)
	Create(ctx context.Context, dto *project.CreateProjectDTO) (*project.Project, error)
	Update(ctx context.Context, id uint64, issue *project.UpdateProjectDTO) (*project.Project, error)
	Remove(ctx context.Context, id uint64) (uint64, error)
}

type repository struct {
	client database.Client
}

func NewRepository(client database.Client) Repository {
	return &repository{
		client: client,
	}
}

func (r *repository) Find(ctx context.Context) ([]*project.Project, error) {
	query := "select id, name, description, key from projects order by id asc"

	rows, err := r.client.Query(ctx, query)
	if err != nil {
		log.Println("rows", err)
		return nil, err
	}

	projects := make([]*project.Project, 0)

	for rows.Next() {
		var n project.Project

		err = rows.Scan(&n.ID, &n.Name, &n.Description, &n.Key)
		if err != nil {
			log.Println("scan", err)
			return nil, err
		}
		projects = append(projects, &n)
	}

	if err = rows.Err(); err != nil {
		log.Println("rows err", err)
		return nil, err
	}

	return projects, nil
}

func (r *repository) FindOne(ctx context.Context, id uint64) (*project.Project, error) {
	query := "select id, name, description from projects where id = $1"

	var n project.Project
	if err := r.client.QueryRow(ctx, query, id).Scan(&n.ID, &n.Name, &n.Description); err != nil {
		fmt.Println(err)
		return nil, err
	}

	return &n, nil
}

func (r *repository) Create(ctx context.Context, dto *project.CreateProjectDTO) (*project.Project, error) {
	q := "insert into projects (name, description, key) values ($1, $2, $3) returning id, name, description, key"

	var p project.Project

	key := trimString(dto.Name)

	if err := r.client.QueryRow(ctx, q, dto.Name, dto.Description, key).Scan(&p.ID, &p.Name, &p.Description, &p.Key); err != nil {
		fmt.Println(fmt.Sprintf("error %v", err))
		return nil, err
	}
	return &p, nil
}

func (r *repository) Update(ctx context.Context, id uint64, dto *project.UpdateProjectDTO) (*project.Project, error) {
	q := "update projects set name = $1, description = $2 where id = $3 returning id, name, description, key"

	var p project.Project

	if err := r.client.QueryRow(ctx, q, dto.Name, dto.Description, id).Scan(&p.ID, &p.Name, &p.Description, &p.Key); err != nil {
		fmt.Println(fmt.Sprintf("error %v", err))
		return nil, err
	}

	return &p, nil
}

func (r *repository) Remove(ctx context.Context, id uint64) (uint64, error) {
	q := "delete from projects where id=$1"

	tag, err := r.client.Exec(ctx, q, id)
	if err != nil {
		log.Println("exec error", err)
		return 0, err
	}

	rowsAffected := tag.RowsAffected()

	if rowsAffected == 0 {
		return 0, errors.New("project not found")
	}

	return uint64(rowsAffected), nil
}

func trimString(str string) string {
	runeStr := []rune(str)
	var res string

	if len(runeStr) > 3 {
		res = string(runeStr[:3])
	} else {
		res = string(runeStr)
	}

	return strings.ToUpper(res)
}
