package nrod

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
	"time"

	"git.fjla.uk/owlboard/timetable-mgr/helpers"
	"git.fjla.uk/owlboard/timetable-mgr/log"
	"go.uber.org/zap"
)

// Downloads NROD Data over HTTP from the given URL, extracted data is returned
func NrodDownload(url string, cfg *helpers.Configuration) ([]byte, error) {
	log.Msg.Debug("Fetching NROD data", zap.String("Request URL", url))
	client := http.Client{
		Timeout: time.Second * 10,
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Msg.Error("Error creating HTTP Request", zap.String("Request URL", url), zap.Error(err))
		return nil, err
	}
	req.Header.Add("Authorization", "Basic "+helpers.BasicAuth(cfg.NrodUser, cfg.NrodPass))

	resp, err := client.Do(req)
	if err != nil {
		log.Msg.Error("Error carrying out HTTP Request", zap.String("Request URL", url), zap.Error(err))
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		err := fmt.Errorf("unexpected status code: %d", resp.StatusCode)
		log.Msg.Error("Non-Successful status code from http response", zap.String("Request URL", url), zap.Error(err))
		return nil, err
	}

	// Yes, I know `readedData` is not proper English.  But readData reads more like a verb action.
	readedData, err := nrodExtract(*resp)
	if err != nil {
		log.Msg.Error("Unable to read response data")
		return nil, err
	}

	return readedData, nil
}

// Extracts GZIP Data from an HTTP Response and returns the decompresses data as a byte array
func nrodExtract(resp http.Response) ([]byte, error) {
	log.Msg.Debug("Extracting HTTP Response Data")
	gzReader, err := gzip.NewReader(resp.Body)
	if err != nil {
		log.Msg.Warn("Unable to create GZIP Reader, data probably not GZIPPED")
		data, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Msg.Error("Unable to read response body")
			return nil, err
		}
		return data, nil
	}

	defer gzReader.Close()

	log.Msg.Debug("GZIP Reader Opened")
	extractedData, err := io.ReadAll(gzReader)
	if err != nil {
		log.Msg.Error("Failed to read GZIPped data", zap.Error(err))
	}

	return extractedData, nil
}
