package repository

import (
	"context"

	sq "github.com/Masterminds/squirrel"

	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helper/errors"
	"git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/model"
)

const tagTableName = "tag"

type TagRepository struct {
	db DB
}

func InitTagRepository(db DB) *TagRepository {
	return &TagRepository{db: db}
}

func (t TagRepository) GetAllUsed(
	ctx context.Context,
	lang model.LanguageID,
) ([]model.Tag, error) {
	var res []model.Tag

	q := "SELECT t.id, t.url, t.tag " +
		"FROM " + articleTagTableName + " at, " + tagTableName + " t " +
		"WHERE t.id = at.tag_id AND at.article_id IN " +
		"(SELECT id FROM " + articleTableName + " " + "WHERE is_active = true AND language = ?) " +
		"GROUP BY t.id"

	err := t.db.SelectContext(ctx, &res, q, uint64(lang))
	if err != nil {
		return nil, errors.Wrap(err, "select")
	}

	return res, nil
}

func (t TagRepository) GetByURL(
	ctx context.Context,
	url string,
) (*model.Tag, error) {
	var res model.Tag

	q, v, err := sq.Select("id", "url", "tag").
		From(tagTableName).
		PlaceholderFormat(placeholder).
		Where(sq.Eq{"url": url}).
		Limit(1).
		ToSql()

	if err != nil {
		return nil, errors.Wrap(err, "build query")
	}

	err = t.db.GetContext(ctx, &res, q, v...)
	if err != nil {
		return nil, errors.Wrap(err, "get")
	}

	return &res, nil
}

func (t TagRepository) GetByID(
	ctx context.Context,
	id uint64,
) (*model.Tag, error) {
	var res model.Tag

	q, v, err := sq.Select("id", "url", "tag").
		From(tagTableName).
		PlaceholderFormat(placeholder).
		Where(sq.Eq{"id": id}).
		Limit(1).
		ToSql()

	if err != nil {
		return nil, errors.Wrap(err, "build query")
	}

	err = t.db.GetContext(ctx, &res, q, v...)
	if err != nil {
		return nil, errors.Wrap(err, "get")
	}

	return &res, nil
}

func (t TagRepository) GetAll(ctx context.Context) ([]model.Tag, error) {
	var res []model.Tag

	q, v, err := sq.Select("id", "url", "tag").
		From(tagTableName).
		ToSql()

	if err != nil {
		return nil, errors.Wrap(err, "build query")
	}

	err = t.db.SelectContext(ctx, &res, q, v...)
	if err != nil {
		return nil, errors.Wrap(err, "select")
	}

	return res, nil
}

func (t TagRepository) GetByArticleID(
	ctx context.Context,
	id uint64,
) ([]model.Tag, error) {
	var res []model.Tag

	q := "SELECT t.id, t.url, t.tag " +
		"FROM " + articleTagTableName + " at, " + tagTableName + " t " +
		"WHERE t.id = at.tag_id AND at.article_id = ?"

	err := t.db.SelectContext(ctx, &res, q, id)
	if err != nil {
		return nil, errors.Wrap(err, "select")
	}

	return res, nil
}

func (t TagRepository) IsUsed(
	ctx context.Context,
	id uint64,
) (bool, error) {
	query, args, err := sq.Select("COUNT(tag_id)").
		From(articleTagTableName).
		PlaceholderFormat(placeholder).
		Where(sq.Eq{"tag_id": id}).
		ToSql()

	if err != nil {
		return false, errors.Wrap(err, "build query")
	}

	var count int
	err = t.db.QueryRowContext(ctx, query, args...).Scan(&count)
	if err != nil {
		return false, err
	}

	return count > 0, nil
}

func (t TagRepository) Add(
	ctx context.Context,
	req model.Tag,
) error {
	q, v, err := sq.Insert(tagTableName).
		PlaceholderFormat(placeholder).
		Columns("tag", "url").
		Values(req.Tag, req.URL).
		ToSql()

	if err != nil {
		return errors.Wrap(err, "build query")
	}

	_, err = t.db.ExecContext(ctx, q, v...)
	if err != nil {
		return errors.Wrap(err, "exec")
	}

	return nil
}

func (t TagRepository) Update(
	ctx context.Context,
	req model.Tag,
) error {
	q, v, err := sq.Update(tagTableName).
		PlaceholderFormat(placeholder).
		Set("tag", req.Tag).
		Set("url", req.URL).
		Where(sq.Eq{"id": req.ID}).
		ToSql()

	if err != nil {
		return errors.Wrap(err, "build query")
	}

	_, err = t.db.ExecContext(ctx, q, v...)
	if err != nil {
		return errors.Wrap(err, "exec")
	}

	return nil
}

func (t TagRepository) Delete(
	ctx context.Context,
	id uint64,
) error {
	q, v, err := sq.Delete(tagTableName).
		PlaceholderFormat(placeholder).
		Where(sq.Eq{"id": id}).
		ToSql()

	if err != nil {
		return errors.Wrap(err, "build query")
	}

	_, err = t.db.ExecContext(ctx, q, v...)
	if err != nil {
		return errors.Wrap(err, "exec")
	}

	return nil
}
