package bcbsi

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/xml"
	"errors"
	"fmt"
	"html"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/pquerna/ffjson/ffjson"

	"bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi/bcbsimodel"

	"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
	"bitbucket.org/nemt/nemt-portal-api/infra/config"
)

type bxeService struct {
	cfg *config.Config
}

func newBXEService(cfg *config.Config) *bxeService {
	return &bxeService{
		cfg: cfg,
	}
}

func (s bxeService) getSignature(APIKey string, secretKey string) string {
	rawKey := fmt.Sprintf("%s%s%v", APIKey, secretKey, time.Now().Unix())

	hasher := md5.New()
	hasher.Write([]byte(rawKey))
	key := hex.EncodeToString(hasher.Sum(nil))

	return strings.ToLower(key)
}

func (s bxeService) GetPayerDetails(subscriberID string) ([]bcbsimodel.EntityResponse, error) {
	apiKey := s.cfg.Blue365.APIKey
	secretKey := s.cfg.Blue365.Secret
	URL := s.cfg.Blue365.URL
	prefix := subscriberID[:3]

	client := &http.Client{}
	req, _ := http.NewRequest("GET", URL+"/"+prefix, bytes.NewBuffer([]byte{}))
	req.Header.Add("X-Api-Key", apiKey)
	req.Header.Add("X-Signature", s.getSignature(apiKey, secretKey))

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error Blue365: ", err)
		return nil, err
	}
	defer resp.Body.Close()
	bReturn, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error Blue365: ", err)
		return nil, err
	}

	var response []bcbsimodel.EntityResponse
	err = ffjson.Unmarshal(bReturn, &response)
	if err != nil {
		fmt.Println("Error Blue365: ", err)
		return nil, err
	}

	return response, nil
}

func (s bxeService) Get271(eligibility viewmodel.Eligibility) (bcbsimodel.Interchange271, error) {
	resp, err := s.CheckEligibility(eligibility)
	if err != nil {
		return bcbsimodel.Interchange271{}, err
	}

	if resp.QueryResult.HIPPA271.T271 != "" {
		xmlString := html.UnescapeString(resp.QueryResult.HIPPA271.T271)
		xmlReader := strings.NewReader(xmlString)

		var f bcbsimodel.Interchange271
		err = xml.NewDecoder(xmlReader).Decode(&f)
		if err != nil {
			fmt.Println("Error to unmarshal: ", err.Error())
			return bcbsimodel.Interchange271{}, err
		}

		return f, nil
	} else {
		return bcbsimodel.Interchange271{}, errors.New("No 271 response")
	}
}

func (s bxeService) CheckEligibility(eligibility viewmodel.Eligibility) (bcbsimodel.MemberEligibilityResponse, error) {
	if eligibility.Payer.PayerID == "" || eligibility.Payer.PayerName == "" {
		eligibility.Payer.PayerID = "621"
		eligibility.Payer.PayerName = "blue_cross_blue_shield_il"
	}

	envelope := bcbsimodel.GetEnvelope(eligibility)
	apiKey := s.cfg.BXE.APIKey
	secretKey := s.cfg.BXE.Secret

	bObj, err := xml.Marshal(envelope)
	if err != nil {
		return bcbsimodel.MemberEligibilityResponse{}, err
	}

	client := &http.Client{}
	req, _ := http.NewRequest("POST", s.cfg.BXE.URL, bytes.NewBuffer(bObj))
	req.Header.Add("Soapaction", "CheckEligibility")
	req.Header.Add("Content-Type", "text/xml")
	req.Header.Add("X-Api-Key", apiKey)
	req.Header.Add("X-Signature", s.getSignature(apiKey, secretKey))

	eligibilityCall := time.Now()
	fmt.Println(fmt.Sprintf("Start eligibility call %s at %s", eligibility.TrackingID, eligibilityCall.Format("2006-01-02 15:04:05 PM")))
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Error WebService Request: ", err)
		return bcbsimodel.MemberEligibilityResponse{}, err
	}
	fmt.Println(fmt.Sprintf("Getting Response from eligibility call %s at %s, duration %f seconds", eligibility.TrackingID, time.Now().Format("2006-01-02 15:04:05 PM"), time.Since(eligibilityCall).Seconds()))
	defer resp.Body.Close()

	bReturn, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error WebService getting byte: ", err)
		return bcbsimodel.MemberEligibilityResponse{}, err
	}

	sResponse := string(bReturn)
	sResponse = strings.Replace(sResponse, "soapenv:Envelope", "Envelope", -1)
	sResponse = strings.Replace(sResponse, "soapenv:Body", "Body", -1)
	sResponse = strings.Replace(sResponse, "ns2:", "", -1)

	var result bcbsimodel.EnvelopeResponse
	err = xml.Unmarshal([]byte(sResponse), &result)
	if err != nil {
		fmt.Println("Error WebService getting object: ", err)
		return bcbsimodel.MemberEligibilityResponse{}, err
	}

	return result.Body.MemberEligibilityResponse, nil
}
