package auth

import (
	"github.com/diamondburned/cchat"
	"github.com/diamondburned/cchat-gtk/internal/gts"
	"github.com/diamondburned/cchat-gtk/internal/ui/dialog"
	"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
	"github.com/diamondburned/cchat-gtk/internal/ui/primitives/spinner"
	"github.com/diamondburned/cchat/text"
	"github.com/diamondburned/handy"
	"github.com/gotk3/gotk3/gtk"
)

type Dialog struct {
	*dialog.Dialog

	header  *gtk.HeaderBar
	backRev *gtk.Revealer
	back    *gtk.Button // might be hidden

	stack *gtk.Stack

	spinner *spinner.Boxed
	leaflet *handy.Leaflet

	stageList *StageList
	request   *RequestStack // might be nil

	Auther cchat.Authenticator
	onAuth func(cchat.Session)
}

// NewDialog makes a new authentication dialog. Auth() is called when the user
// is authenticated successfully inside the Gtk main thread.
func NewDialog(name text.Rich, authers []cchat.Authenticator, auth func(cchat.Session)) *Dialog {
	d := &Dialog{
		Auther: nil,
		onAuth: auth,
	}

	d.spinner = spinner.NewVisible()
	d.spinner.SetSizeRequest(50, 50)
	d.spinner.Stop()
	d.spinner.Show()

	d.request = NewRequestStack()
	d.request.SetHExpand(true)
	d.request.SetName("request")
	d.request.Show()

	d.leaflet = handy.LeafletNew()
	d.leaflet.SetCanSwipeBack(true)
	d.leaflet.SetCanSwipeForward(false)
	d.leaflet.SetVExpand(true)
	d.leaflet.SetTransitionType(handy.LeafletTransitionTypeSlide)
	d.leaflet.Show()

	d.stack, _ = gtk.StackNew()
	d.stack.SetVExpand(true)
	d.stack.SetHExpand(true)
	d.stack.AddNamed(d.leaflet, "leaflet")
	d.stack.AddNamed(d.spinner, "spinner")
	d.stack.SetVisibleChildName("leaflet")
	d.stack.Show()

	d.back, _ = gtk.ButtonNewFromIconName("go-previous-symbolic", gtk.ICON_SIZE_BUTTON)
	d.back.Show()
	d.back.Connect("clicked", func(back *gtk.Button) {
		// If check just in case.
		if d.stageList != nil {
			d.leaflet.SetVisibleChild(d.stageList)
			d.backRev.SetRevealChild(false)
		}
	})

	d.backRev, _ = gtk.RevealerNew()
	d.backRev.SetTransitionDuration(50)
	d.backRev.SetTransitionType(gtk.REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)
	d.backRev.Add(d.back)
	d.backRev.Show()

	d.header, _ = gtk.HeaderBarNew()
	d.header.SetShowCloseButton(true)
	d.header.SetTitle("Log in to " + name.Content)
	d.header.PackStart(d.backRev)
	d.header.Show()

	d.setAuthers(authers)

	primitives.LeafletOnFold(d.leaflet, func(folded bool) {
		visibleChildName := primitives.GetName(d.leaflet.GetVisibleChild().ToWidget())

		if folded && visibleChildName == "request" {
			d.backRev.SetRevealChild(true)
		} else {
			d.backRev.SetRevealChild(false)
		}
	})

	d.Dialog = dialog.NewCSD(d.stack, d.header)
	d.Dialog.SetDefaultSize(500, 350)
	d.Dialog.Show()

	return d
}

func (d *Dialog) setAuthers(authers []cchat.Authenticator) {
	primitives.RemoveChildren(d.leaflet)

	d.request.SetRequest(nil, nil)

	d.stageList = NewStageList(authers, d.setAuther)
	d.stageList.SetName("stagelist")
	d.stageList.Show()

	d.leaflet.Add(d.stageList)
	d.leaflet.Add(d.request)

	d.stageList.SelectFirst()
	d.leaflet.SetVisibleChild(d.stageList)
	d.backRev.SetRevealChild(false)
}

func (d *Dialog) setAuther(auther cchat.Authenticator) {
	d.Auther = auther
	d.request.SetRequest(auther, d.onContinue)
	d.backRev.SetRevealChild(d.leaflet.GetFolded())
	d.leaflet.SetVisibleChild(d.request)
}

func (d *Dialog) onContinue() {
	request := d.request.Request()
	values := request.values()
	auther := d.Auther

	d.Dialog.SetSensitive(false)
	d.back.Hide()
	d.stack.SetVisibleChildName("spinner")
	d.spinner.Start()

	gts.Async(func() (func(), error) {
		s, err := auther.Authenticate(values)

		return func() {
			if err == nil {
				d.Destroy()
				d.onAuth(s)
				return
			}

			d.spinner.Stop()
			d.stack.SetVisibleChildName("leaflet")
			d.back.Show()
			d.Dialog.SetSensitive(true)

			if nextStage := err.NextStage(); nextStage != nil {
				d.setAuthers(nextStage)
			} else {
				request.SetError(err)
			}
		}, err
	})
}
