package amo

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"golang.org/x/oauth2"
	"net/http"
)

const (
	OAUTH_URL = "https://www.amocrm.ru/oauth"
)

type Client struct {
	App        *ClientApp
	HTTPClient *http.Client
	Subdomain  string // Субдомен с которым работаем
	Code       string // Код авторизации
	Token      *oauth2.Token
}

type ClientApp struct {
	Config *oauth2.Config
}

func NewClientApp(clientID, clientSecret string, redirectUri string) *ClientApp {
	return &ClientApp{
		Config: &oauth2.Config{
			ClientID:     clientID,
			ClientSecret: clientSecret,
			Endpoint: oauth2.Endpoint{
				AuthURL:  OAUTH_URL,
				TokenURL: OAUTH_URL + "/token",
			},
			RedirectURL: redirectUri,
			Scopes:      nil,
		},
	}
}

func (ca *ClientApp) GenerateOAuthUrl() string {
	return ca.Config.AuthCodeURL(
		"my-state",
		oauth2.SetAuthURLParam("mode", "popup"),
	)
}

func (ca *ClientApp) DecodeJwt(r *http.Request) (*XAuthToken, error) {
	tokenHeader := r.Header.Get("x-auth-token")

	if tokenHeader == "" {
		return nil, errors.New("empty jwt")
	}

	token, err := jwt.ParseWithClaims(tokenHeader, &XAuthToken{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(ca.Config.ClientSecret), nil
	})

	if err != nil {
		return nil, err
	}

	claims, ok := token.Claims.(*XAuthToken)
	if !ok || !token.Valid {
		fmt.Println("token:", token)
		fmt.Println("claims:", claims)
		fmt.Println("valid:", token.Valid)
		return nil, errors.New("invalid token")
	}

	return claims, nil
}

func (ca *ClientApp) NewClient(ctx context.Context, subdomain string, token *oauth2.Token, code string) (*Client,
	error) {
	var err error

	subdomain = "https://" + subdomain

	ca.Config.Endpoint.TokenURL = subdomain

	if code != "" {
		token, err = ca.Config.Exchange(ctx, code)
	}

	return &Client{
		App:        ca,
		HTTPClient: ca.Config.Client(ctx, token),
		Subdomain:  subdomain,
		Token:      token,
	}, err
}

// AuthCode - обменивает код авторизации на токен
//func (c *Client) AuthCode() (*oauth2.Token, error) {
//	body := map[string]string{
//		"client_id":     c.App.ClientID,
//		"client_secret": c.App.ClientSecret,
//		"grant_type":    "authorization_code",
//		"code":          c.Code,
//		"redirect_uri":  c.App.RedirectUri,
//	}
//
//	bytesBody, err := json.Marshal(body)
//	if err != nil {
//		return nil, err
//	}
//
//	req, err := http.NewRequest("POST", c.Subdomain+"/oauth2/access_token", bytes.NewBuffer(bytesBody))
//	if err != nil {
//		return nil, err
//	}
//
//	c.setHeaders(req)
//
//	resp, err := c.HTTPClient.Do(req)
//
//	if err != nil {
//		return nil, err
//	}
//
//	var response RespAuthCode
//	err = json.NewDecoder(resp.Body).Decode(&response)
//	if err != nil {
//		return nil, err
//	}
//
//	c.Token = &oauth2.Token{
//		AccessToken:  response.AccessToken,
//		TokenType:    response.TokenType,
//		RefreshToken: response.RefreshToken,
//		Expiry:       time.Now().Add(time.Duration(response.ExpiresIn) * time.Second),
//	}
//
//	return c.Token, nil
//}

func (c *Client) GetAccount() (*Account, error) {
	req, err := http.NewRequest("GET", c.Subdomain+"/api/v4/account", nil)
	if err != nil {
		fmt.Println("1")
		return nil, err
	}

	c.setHeaders(req)

	resp, err := c.HTTPClient.Do(req)

	if err != nil {
		fmt.Println("2")
		return nil, err
	}

	var response Account
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		fmt.Println("3")
		return nil, err
	}

	return &response, nil
}

func (c *Client) GetLeadById(id string) (*Lead, error) {
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%v/api/v4/leads/%v?with=contacts,catalog_elements,is_price_modified_by_robot,loss_reason",
			c.Subdomain, id),
		nil,
	)

	fmt.Println("URL:", fmt.Sprintf("%v/api/v4/leads/%v?with=contacts,catalog_elements,is_price_modified_by_robot,"+
		"loss_reason",
		c.Subdomain, id))

	if err != nil {
		fmt.Println("1")
		return nil, err
	}

	c.setHeaders(req)

	resp, err := c.HTTPClient.Do(req)

	if err != nil {
		fmt.Println("2")
		return nil, err
	}

	var response Lead
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		return nil, err
	}

	return &response, nil
}

func (c *Client) GetContactById(id string) (*Contact, error) {
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%v/api/v4/contacts/%v?with=catalog_elements,leads,customers",
			c.Subdomain, id),
		nil,
	)
	if err != nil {
		fmt.Println("1")
		return nil, err
	}

	c.setHeaders(req)

	resp, err := c.HTTPClient.Do(req)

	if err != nil {
		fmt.Println("2")
		return nil, err
	}

	var response Contact
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		return nil, err
	}

	return &response, nil
}

func (c *Client) GetCompanyById(id string) (*Company, error) {
	req, err := http.NewRequest("GET",
		fmt.Sprintf("%v/api/v4/companies/%v?with=contacts,leads,catalog_elements,customers",
			c.Subdomain, id),
		nil,
	)
	if err != nil {
		fmt.Println("1")
		return nil, err
	}

	c.setHeaders(req)

	resp, err := c.HTTPClient.Do(req)

	if err != nil {
		fmt.Println("2")
		return nil, err
	}

	var response Company
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		return nil, err
	}

	return &response, nil
}

func (c *Client) setHeaders(req *http.Request) {
	req.Header.Set("User-Agent", "amoCRM-oAuth-client/1.0")
	req.Header.Set("Content-Type", "application/json")

	//if c.Token != nil {
	//	c.Token.SetAuthHeader(req)
	//}
}
