package graph

import (
	"context"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/dsoprea/go-exif/v3"
	exifcommon "github.com/dsoprea/go-exif/v3/common"
	"github.com/google/uuid"
	"reichard.io/imagini/graph/model"
)

func getContextHTTP(ctx context.Context) (*http.ResponseWriter, *http.Request, error) {
	authContext := ctx.Value("auth").(*model.AuthContext)

	resp := authContext.AuthResponse
	if resp == nil {
		return nil, nil, errors.New("Context Error")
	}

	req := authContext.AuthRequest
	if resp == nil {
		return nil, nil, errors.New("Context Error")
	}

	return resp, req, nil
}

func getContextIDs(ctx context.Context) (string, string, error) {
	authContext := ctx.Value("auth").(*model.AuthContext)
	accessToken := *authContext.AccessToken

	uid, ok := accessToken.Get("sub")
	if !ok {
		return "", "", errors.New("Context Error")
	}

	did, ok := accessToken.Get("did")
	if !ok {
		return "", "", errors.New("Context Error")
	}

	userID, err := uuid.Parse(uid.(string))
	if err != nil {
		return "", "", errors.New("Context Error")
	}

	deviceID, err := uuid.Parse(did.(string))
	if err != nil {
		return "", "", errors.New("Context Error")
	}

	return userID.String(), deviceID.String(), nil
}

func deriveDeviceType(r *http.Request) model.DeviceType {
	userAgent := strings.ToLower(r.Header.Get("User-Agent"))
	if strings.Contains(userAgent, "ios-imagini") {
		return model.DeviceTypeIOs
	} else if strings.Contains(userAgent, "android-imagini") {
		return model.DeviceTypeAndroid
	} else if strings.Contains(userAgent, "chrome") {
		return model.DeviceTypeChrome
	} else if strings.Contains(userAgent, "firefox") {
		return model.DeviceTypeFirefox
	} else if strings.Contains(userAgent, "msie") {
		return model.DeviceTypeInternetExplorer
	} else if strings.Contains(userAgent, "edge") {
		return model.DeviceTypeEdge
	} else if strings.Contains(userAgent, "safari") {
		return model.DeviceTypeSafari
	}
	return model.DeviceTypeUnknown
}

func mediaItemFromEXIFData(filePath string) (*model.MediaItem, error) {
	rawExif, err := exif.SearchFileAndExtractExif(filePath)
	entries, _, err := exif.GetFlatExifData(rawExif, nil)

	decLong := float64(1)
	decLat := float64(1)

	mediaItem := &model.MediaItem{}
	for _, v := range entries {
		if v.TagName == "DateTimeOriginal" {
			formattedTime, _ := time.Parse("2006:01:02 15:04:05", v.Formatted)
			mediaItem.ExifDate = &formattedTime
		} else if v.TagName == "GPSLatitude" {
			latStruct := v.Value.([]exifcommon.Rational)
			decLat *= deriveDecimalCoordinate(
				latStruct[0].Numerator/latStruct[0].Denominator,
				latStruct[1].Numerator/latStruct[1].Denominator,
				float64(latStruct[2].Numerator)/float64(latStruct[2].Denominator),
			)
		} else if v.TagName == "GPSLongitude" {
			longStruct := v.Value.([]exifcommon.Rational)
			decLong *= deriveDecimalCoordinate(
				longStruct[0].Numerator/longStruct[0].Denominator,
				longStruct[1].Numerator/longStruct[1].Denominator,
				float64(longStruct[2].Numerator)/float64(longStruct[2].Denominator),
			)
		} else if v.TagName == "GPSLatitudeRef" && v.Formatted == "S" {
			decLat *= -1
		} else if v.TagName == "GPSLongitudeRef" && v.Formatted == "W" {
			decLong *= -1
		}
	}

	mediaItem.Latitude = &decLat
	mediaItem.Longitude = &decLong

	return mediaItem, err
}

func deriveDecimalCoordinate(degrees, minutes uint32, seconds float64) float64 {
	return float64(degrees) + (float64(minutes) / 60) + (seconds / 3600)
}
