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 articleTableName = "article"

var articleTableColumns = []string{
	"id",
	"url",
	"publish_time",
	"title",
	"image",
	"text",
	"preview_text",
	"meta_keywords",
	"meta_desc",
	"is_active",
	"language",
}

type ArticleRepository struct {
	db DB
}

func InitArticleRepository(db DB) *ArticleRepository {
	return &ArticleRepository{db: db}
}

func (a ArticleRepository) GetAllPreview(
	ctx context.Context,
	lang model.LanguageID,
) ([]model.ArticlePreview, error) {
	var res []model.ArticlePreview

	q, v, err := sq.Select("id", "url", "publish_time", "title", "preview_text", "image").
		From(articleTableName).
		PlaceholderFormat(placeholder).
		Where(sq.Eq{"is_active": true, "language": uint64(lang)}).
		OrderBy("publish_time DESC").
		ToSql()

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

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

	return res, nil
}

func (a ArticleRepository) GetAll(ctx context.Context) ([]model.Article, error) {
	var res []model.Article

	q, v, err := sq.Select(articleTableColumns...).
		From(articleTableName).
		OrderBy("publish_time DESC").
		ToSql()

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

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

	return res, nil
}

func (a ArticleRepository) GetPreviewByTagID(
	ctx context.Context,
	tagID uint64,
	lang model.LanguageID,
) ([]model.ArticlePreview, error) {
	var res []model.ArticlePreview

	q := "SELECT a.id, a.url, a.publish_time, a.title, a.preview_text, a.image " +
		"FROM " + articleTableName + " a, " + articleTagTableName + " at " +
		"WHERE a.is_active = true AND a.language = ? AND at.article_id = a.id AND at.tag_id = ? " +
		"ORDER BY a.publish_time DESC"

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

	return res, nil
}

func (a ArticleRepository) GetByURL(
	ctx context.Context,
	url string,
	lang model.LanguageID,
) (*model.Article, error) {
	var res model.Article

	q, v, err := sq.Select(articleTableColumns...).
		From(articleTableName).
		PlaceholderFormat(placeholder).
		Where(sq.Eq{"url": url, "language": lang}).
		Limit(1).
		ToSql()

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

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

	return &res, nil
}

func (a ArticleRepository) GetByID(
	ctx context.Context,
	id uint64,
) (*model.Article, error) {
	var res model.Article

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

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

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

	return &res, nil
}

func (a ArticleRepository) Add(
	ctx context.Context,
	m model.Article,
) (uint64, error) {
	q, v, err := sq.Insert(articleTableName).
		PlaceholderFormat(placeholder).
		Columns(
			"url",
			"publish_time",
			"title",
			"image",
			"text",
			"preview_text",
			"meta_keywords",
			"meta_desc",
			"is_active",
			"language",
		).Values(
		m.URL,
		m.PublishTime,
		m.Title,
		m.Image,
		m.Text,
		m.PreviewText,
		m.MetaKeywords,
		m.MetaDescription,
		m.IsActive,
		m.Language,
	).
		ToSql()

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

	res, err := a.db.ExecContext(ctx, q, v...)
	if err != nil {
		return 0, errors.Wrap(err, "exec")
	}

	id, err := res.LastInsertId()
	if err != nil {
		return 0, errors.Wrap(err, "can't get last insert id")
	}

	return uint64(id), nil
}

func (a ArticleRepository) Update(
	ctx context.Context,
	req model.Article,
) error {
	q, v, err := sq.Update(articleTableName).
		PlaceholderFormat(placeholder).
		Set("url", req.URL).
		Set("publish_time", req.PublishTime).
		Set("title", req.Title).
		Set("image", req.Image).
		Set("text", req.Text).
		Set("preview_text", req.PreviewText).
		Set("meta_keywords", req.MetaKeywords).
		Set("meta_desc", req.MetaDescription).
		Set("is_active", req.IsActive).
		Set("language", req.Language).
		Where(sq.Eq{"id": req.ID}).
		ToSql()

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

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

	return nil
}

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

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

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

	return nil
}
