package postgres

import (
	"context"
	"errors"
	"fmt"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal/model"
	rs "github.com/danilsolovyov/reflectgostructv1"
	"github.com/jackc/pgx/v4/pgxpool"
	"strconv"
	"strings"
	"time"
)

type YaDisk struct {
	conn *pgxpool.Pool
}

func InitYaDisk(ctx context.Context, conn *pgxpool.Pool) *YaDisk {
	d := &YaDisk{conn: conn}
	err := d.init(ctx)
	if err != nil {
		//glg.Error("ErrInitYaDisk", err)
		return nil
	}

	return d
}

func (d *YaDisk) init(ctx context.Context) error {
	s := rs.PsqlTagToSql(&model.YaDisk{})
	sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS \"YaDisk\" (%v);", s)
	_, err := d.conn.Exec(ctx, sql)
	return err
}

func (d *YaDisk) Insert(ctx context.Context, record *model.YaDisk) (int, error) {
	now := time.Now().UTC()
	record.CreatedAt = now
	record.UpdatedAt = now
	record.IsDeleted = false

	conn, err := d.conn.Acquire(ctx)

	if err != nil {
		conn.Release()
		return 0, err
	}

	//Find YaDisk by user_id
	sql := fmt.Sprintf("SELECT id FROM \"YaDisk\" WHERE user_id = '%v'", record.UserID)

	var foundID int
	err = conn.QueryRow(ctx, sql).Scan(&foundID)
	if err != nil {
		if err.Error() != ErrorNotFound.Error() {
			return 0, err
		}
	}

	if foundID > 0 {
		fmt.Println("YaDisk already exists", foundID)
		err = errors.New("YaDisk already exists")
		return 0, err
	}

	tags, values := rs.GetPsqlTagsAndValues(record)
	sql = fmt.Sprintf("INSERT INTO \"YaDisk\" (%v) VALUES (%v) RETURNING id;", tags, values)

	var id int
	err = conn.QueryRow(ctx, sql).Scan(&id)
	conn.Release()

	if err != nil {
		return 0, err
	}

	return id, nil
}

func (d *YaDisk) InsertOrUpdate(ctx context.Context, record *model.YaDisk) (string, error) {
	now := time.Now().UTC()
	record.CreatedAt = now
	record.UpdatedAt = now
	record.IsDeleted = false

	conn, err := d.conn.Acquire(ctx)

	if err != nil {
		conn.Release()
		return "", err
	}

	//Find YaDisk by user_id
	sql := fmt.Sprintf("SELECT id FROM \"YaDisk\" WHERE user_id = '%v'", record.UserID)

	var foundID int
	err = conn.QueryRow(ctx, sql).Scan(&foundID)
	if err != nil {
		fmt.Println("errr", err)
		if err.Error() != ErrorNotFound.Error() {
			return "", err
		}
	}

	tags, values := rs.GetPsqlTagsAndValues(record)
	var id int

	if foundID > 0 {
		sql = fmt.Sprintf("UPDATE \"YaDisk\" SET (%v) = (%v) WHERE id = %v;", tags, values, record.ID)
		id = foundID
	} else {
		sql = fmt.Sprintf("INSERT INTO \"YaDisk\" (%v) VALUES (%v) RETURNING id;", tags, values)
	}

	err = conn.QueryRow(ctx, sql).Scan(&id)
	conn.Release()

	if err != nil {
		return "", err
	}

	return strconv.Itoa(id), nil
}

func (d *YaDisk) GetByID(ctx context.Context, id string) (*model.YaDisk, error) {
	var result model.YaDisk

	conn, err := d.conn.Acquire(ctx)
	if err != nil {
		conn.Release()
		return nil, err
	}

	sql := fmt.Sprintf("SELECT * FROM \"YaDisk\" WHERE id = %v", id)
	rows, err := conn.Query(ctx, sql)
	conn.Release()
	if err != nil {
		return nil, err
	}

	for rows.Next() {
		err = rows.Scan(&result.ID, &result.UserID, &result.AccessToken, &result.TemplateFolder,
			&result.SaveFolder, &result.ExpiresIn, &result.IsDeleted, &result.CreatedAt, &result.UpdatedAt)
		if err != nil {
			return nil, err
		}
	}

	return &result, err
}

func (d *YaDisk) GetByFilter(ctx context.Context, start, count int, needle map[string]string) ([]model.YaDisk, error) {
	var result []model.YaDisk

	conn, err := d.conn.Acquire(ctx)
	if err != nil {
		conn.Release()
		return nil, err
	}

	needleToSql := ""
	if len(needle) > 0 {
		needleToSql += " AND "
		i := 0
		for k, v := range needle {
			v = strings.ReplaceAll(v, "'", "''")
			needleToSql += fmt.Sprintf("%v = %v", k, v)
			if i < len(needle) {
				needleToSql += " AND "
			}
			i++
		}
	}

	sql := fmt.Sprintf("SELECT * FROM \"YaDisk\" WHERE (id >= %v %v) ORDER BY id LIMIT %v;",
		start,
		needleToSql,
		count,
	)
	rows, err := conn.Query(ctx, sql)
	conn.Release()

	if err != nil {
		return nil, err
	}

	if rows == nil {
		err = ErrorGotEmptyRow
		return nil, err
	}

	for rows.Next() {
		var u model.YaDisk
		err = rows.Scan(&u.ID, &u.UserID, &u.AccessToken, &u.TemplateFolder,
			&u.SaveFolder, &u.ExpiresIn, &u.IsDeleted, &u.CreatedAt, &u.UpdatedAt)
		if err != nil {
			return nil, err
		}
		result = append(result, u)
	}

	return result, nil
}

func (d *YaDisk) UpdateByID(ctx context.Context, record *model.YaDisk) error {
	record.UpdatedAt = time.Now().UTC()

	conn, err := d.conn.Acquire(ctx)
	if err != nil {
		conn.Release()
		return err
	}

	tags, values := rs.GetPsqlTagsAndValues(record)
	sql := fmt.Sprintf("UPDATE \"YaDisk\" SET (%v) = (%v) WHERE id = %v;", tags, values, record.ID)
	err = conn.QueryRow(ctx, sql).Scan()
	conn.Release()

	if err != nil {
		return err
	}

	return nil
}

func (d *YaDisk) DeleteByID(ctx context.Context, id string) error {
	conn, err := d.conn.Acquire(ctx)
	if err != nil {
		conn.Release()
		return err
	}

	sql := fmt.Sprintf("UPDATE \"YaDisk\" SET is_deleted = true WHERE id = %v", id)
	err = conn.QueryRow(ctx, sql).Scan()
	conn.Release()
	if err != nil {
		return err
	}

	return nil
}
