package issue

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

type Repository interface {
	Find(ctx context.Context) ([]*issue.Issue, error)
	FindOne(ctx context.Context, id uint64) (*issue.Issue, error)
	Create(ctx context.Context, dto *issue.CreateIssueDTO) (*uint64, error)
	Update(ctx context.Context, id uint64, issue *issue.Issue) error
	Remove(ctx context.Context, id uint64) (uint64, error)
	UpdatePositions(ctx context.Context, position int, statusId uint32, id uint32) (*uint32, error)
}

type repository struct {
	client database.Client
}

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

func (r *repository) Find(ctx context.Context) ([]*issue.Issue, error) {
	q := `select 
    	i."id", 
    	i."name", 
    	i."description", 
    	i."position", 
    	i."created", 
    	i."project_id", 
    	i."status_id", 
    	p."id" as project_id, 
    	p."name" as project_name, 
    	p."description" as project_description, 
    	p."key" as project_key, 
    	s."id" as status_id, 
    	s."name" as status_name, 
    	s."description" as status_description, 
    	s."position" as position 
	from issues i 
	join projects p on p.id = project_id
	join statuses s on s.id = status_id
	order by i.id asc`

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

	issues := make([]*issue.Issue, 0)

	for rows.Next() {
		var n issue.Issue
		var p project.Project
		var s status.Status

		err = rows.Scan(
			&n.ID,
			&n.Name,
			&n.Description,
			&n.Position,
			&n.Created,
			&n.ProjectID,
			&n.StatusID,
			&p.ID,
			&p.Name,
			&p.Description,
			&p.Key,
			&s.ID,
			&s.Name,
			&s.Description,
			&s.Position,
		)
		if err != nil {
			log.Println("scan", err)
			return nil, err
		}
		n.Project = p
		n.Status = s
		issues = append(issues, &n)
	}

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

	return issues, nil
}

func (r *repository) UpdatePositions(ctx context.Context, position int, statusId uint32, id uint32) (*uint32, error) {
	q := `update issues set "position" = $1, "status_id"=$2 where "id"=$3 returning id`

	var resultId uint32

	if err := r.client.QueryRow(ctx, q, position, statusId, id).Scan(&resultId); err != nil {
		fmt.Println(fmt.Sprintf("error %v", err))
		return nil, err
	}

	return &resultId, nil
}

func (r *repository) FindOne(ctx context.Context, id uint64) (*issue.Issue, error) {
	q := `select 
    	i."id", 
    	i."name", 
    	i."description", 
    	i."position", 
    	i."created", 
    	i."project_id", 
    	i."status_id", 
    	p."id" as project_id, 
    	p."name" as project_name, 
    	p."description" as project_description, 
    	p."key" as project_key, 
    	s."id" as status_id, 
    	s."name" as status_name, 
    	s."description" as status_description, 
    	s."position" as position 
	from issues i 
	join projects p on p.id = project_id
	join statuses s on s.id = status_id
	where i."id" = $1`

	var n issue.Issue
	var p project.Project
	var s status.Status

	if err := r.client.QueryRow(ctx, q, id).Scan(
		&n.ID,
		&n.Name,
		&n.Description,
		&n.Position,
		&n.Created,
		&n.ProjectID,
		&n.StatusID,
		&p.ID,
		&p.Name,
		&p.Description,
		&p.Key,
		&s.ID,
		&s.Name,
		&s.Description,
		&s.Position,
	); err != nil {
		fmt.Println(err)
		return &issue.Issue{}, err
	}

	n.Project = p
	n.Status = s

	return &n, nil
}

func (r *repository) Create(ctx context.Context, dto *issue.CreateIssueDTO) (*uint64, error) {
	q := `insert into issues (name, description, project_id, status_id, position) 
	values ($1, $2, $3, $4, $5) 
	returning id`

	var position uint32 = 0
	var id uint64

	if dto.Position != nil {
		position = *dto.Position
	}

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

func (r *repository) Update(ctx context.Context, id uint64, issue *issue.Issue) error {
	//TODO implement me
	fmt.Println("update", id, issue, ctx)
	panic("implement me")
}

func (r *repository) Remove(ctx context.Context, id uint64) (uint64, error) {
	q := "delete from issues 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
}
