package controller

import (
	"context"
	"fmt"
	"ticket-pimp/internal/domain"
	"ticket-pimp/internal/storage/db"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/jackc/pgx/v5/pgtype"
)

// WriteTaskToDB
/*
Makes an SQL query to create new tasks row from domain.Task entity
	- Creator field: telegram nickname or Discord's Mention();
	- Creator link (tg ID) in telegram case;
	- Description from telegram/discord message bodies;
*/
func (wc *WorkflowController) WriteTaskToDB(t *domain.Task) (*domain.Task, error) {
	dbtask, err := wc.q.InsertTask(context.TODO(), db.InsertTaskParams{
		Creator: pgtype.Text{String: t.Creator, Valid: true},
		CreatorLink: pgtype.Text{
			String: t.CreatorLink,
			Valid:  true,
		},
		Description: pgtype.Text{
			String: t.Description,
			Valid:  true,
		},
	})
	if err != nil {
		return nil, fmt.Errorf("unable to create task at the db: %v", err)
	}
	// ------------------------------------------------------------------------------------

	task := newConvertable(&dbtask).ExtractDomain()
	return task, nil
}

// InitTask
/*
	Runs the following:
		- Use WriteTaskToDB method to make a new task row in the db;
		- init new discord bot instance;

	Possible errors:
		- db record couldn't be created;
		- bot couldn't be inited;
		- bot session couldn't be started;
		- thread couldn't be started;
		- first task message couldn't be edited;
*/
func (wc *WorkflowController) InitTask(t *domain.Task) (*domain.Task, error) {

	task, err := wc.WriteTaskToDB(t)
	if err != nil {
		return nil, fmt.Errorf("unable to create task at the db: %v", err)
	}

	s, err := discordgo.New("Bot " + wc.conf.Discord.Token)
	if err != nil {
		return task, fmt.Errorf("unable to create discord session: %v", err)
	}

	if err := s.Open(); err != nil {
		return task, fmt.Errorf("cannot open the session: %v", err)
	}

	msg := discordgo.MessageSend{
		Content: task.DiscordMessage(domain.NewTaskState()),
		Components: []discordgo.MessageComponent{
			discordgo.ActionsRow{
				Components: []discordgo.MessageComponent{
					discordgo.Button{
						Label:    "Start",
						Style:    discordgo.SuccessButton,
						Disabled: false,
						CustomID: "task_start",
					},
					discordgo.Button{
						Label:    "Close",
						Style:    discordgo.DangerButton,
						Disabled: true,
						CustomID: "task_close",
					},
				},
			},
		},
	}

	th, err := s.ForumThreadStartComplex(
		wc.conf.Discord.IsTaskForum,
		&discordgo.ThreadStart{
			Name:        fmt.Sprintf("Task ID: %d, by %s", task.ID, task.Creator),
			AppliedTags: []string{wc.conf.Discord.Tags[domain.NewTaskState()]},
		},
		&msg,
	)
	if err != nil {
		return task, fmt.Errorf("unable to update channel: %v", err)
	}

	err = wc.UpdateTasksMessageID(context.TODO(), th.ID, task.ID)
	if err != nil {
		return task, fmt.Errorf("unable to set discord message to task: %v", err)
	}

	return task, nil
}

func (wc *WorkflowController) UpdateTasksMessageID(ctx context.Context, msgID string, taskID int32) error {
	err := wc.q.UpdateTaskWithMessageID(context.TODO(), db.UpdateTaskWithMessageIDParams{
		Messageid: pgtype.Text{String: msgID, Valid: true},
		ID:        taskID,
	})
	return err
}

// UpdateTask
/*
	- updates task by message Id
	- with an action:
		0 for 'in progress' state;
		1 for 'done' state;
	- and assignee..
*/
func (wc *WorkflowController) UpdateTask(messageId string, opt int, assignee string) (*TaskConvertable, error) {
	var (
		err    error
		dbtask db.Task
	)

	switch opt {
	case 0:
		dbtask, err = wc.q.StartTask(context.TODO(), db.StartTaskParams{
			UpdatedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true},
			Assignee:  pgtype.Text{String: assignee, Valid: true},
			Messageid: pgtype.Text{String: messageId, Valid: true},
		})
		return &TaskConvertable{&dbtask}, err

	case 1:
		dbtask, err = wc.q.CloseTask(context.TODO(), db.CloseTaskParams{
			DeletedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true},
			Assignee:  pgtype.Text{String: assignee, Valid: true},
			Messageid: pgtype.Text{String: messageId, Valid: true},
		})
		return &TaskConvertable{&dbtask}, err
	}

	return &TaskConvertable{&dbtask}, nil
}
