package oauth2

import (
	"errors"
	"fmt"
	"local/oauth2/oauth2client"
	"local/oauth2/oauth2server/config"
	"local/oauth2/oauth2server/server"
	"net/http"
	"net/http/cookiejar"
	"net/http/httptest"
	"regexp"
	"strings"
	"testing"
)

func TestAll(t *testing.T) {
	oauth2server, err := launchServer()
	if err != nil {
		t.Fatal(err)
	}
	defer oauth2server.Close()

	s := dummyServer(oauth2server.URL)
	defer s.Close()

	if err := createUser(oauth2server.URL); err != nil {
		t.Fatal(err)
	}

	if err := logUser(oauth2server.URL); err != nil {
		t.Fatal(err)
	}

	if err := shouldRedir(s.URL); err != nil {
		t.Fatal(err)
	}

	if err := testAuth(oauth2server.URL, s.URL); err != nil {
		t.Fatal(err)
	}
}

func launchServer() (*httptest.Server, error) {
	config.Store = "map"

	oauth2server := server.New()
	err := oauth2server.Routes()
	if err != nil {
		return nil, err
	}
	s := httptest.NewServer(oauth2server)

	re := regexp.MustCompile(":[0-9]*")
	port := re.FindString(s.URL)
	config.Port = port

	return s, err
}

func dummyServer(oauth2server string) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		err := oauth2client.Authenticate(oauth2server, w, r)
		if err != nil {
			return
		}
		fmt.Fprintln(w, "dummy server serving authenticated")
	}))
}

func createUser(oauth2server string) error {
	resp, err := http.Post(oauth2server+"/users/submit", "application/x-www-form-urlencoded", strings.NewReader("username=abc"))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New("bad status " + resp.Status)
	}
	return nil
}

func logUser(oauth2server string) error {
	resp, err := http.Post(oauth2server+"/authorize", "application/x-www-form-urlencoded", strings.NewReader("username=abc"))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New("bad status " + resp.Status)
	}
	return nil
}

func shouldRedir(dummy string) error {
	c := makeClient()
	return clientShouldRedir(c, dummy)
}

func clientShouldRedir(c *http.Client, dummy string) error {
	req, _ := http.NewRequest("GET", dummy, nil)
	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.Request.URL.Path != "/users/log" {
		return fmt.Errorf("did not need redir without auth: %v", resp.Request.URL)
	}
	return nil
}

func clientShouldNotRedir(c *http.Client, dummy string) error {
	req, _ := http.NewRequest("GET", dummy, nil)
	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.Request.URL.Path == "/users/log" {
		return fmt.Errorf("did redir with auth: %v", resp.Request.URL.Path)
	}
	return nil
}

func testAuth(oauth2server, dummy string) error {
	c := makeClient()
	if err := clientShouldRedir(c, dummy); err != nil {
		return err
	}
	if err := clientLogin(c, oauth2server); err != nil {
		return err
	}
	if err := clientShouldNotRedir(c, dummy); err != nil {
		return err
	}
	return nil
}

func clientLogin(c *http.Client, oauth2server string) error {
	req, _ := http.NewRequest("POST", oauth2server+"/authorize", strings.NewReader("username=abc"))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	resp, err := c.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("bad status; %v", resp.StatusCode)
	}
	if resp.Request.URL.Path != "/" {
		return fmt.Errorf("login response path wrong: %v", resp.Request.URL)
	}
	return nil
}

func makeClient() *http.Client {
	jar, _ := cookiejar.New(&cookiejar.Options{})
	return &http.Client{
		Jar: jar,
	}
}
