package face

import (
	"context"
	"fmt"
	"image"
	_ "image/jpeg"
	_ "image/png"
	"bytes"
	"io"
	"mime/multipart"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/rekognition"
	"github.com/aws/aws-sdk-go-v2/service/rekognition/types"
)

// RekognitionClient wraps AWS Rekognition operations
type RekognitionClient struct {
	client       *rekognition.Client
	collectionID string
}

// FaceDetection represents a detected face
type FaceDetection struct {
	Confidence float64     `json:"confidence"`
	BoundingBox BoundingBox `json:"boundingBox"`
}

// BoundingBox represents face location in image
type BoundingBox struct {
	Left   float64 `json:"left"`
	Top    float64 `json:"top"`
	Width  float64 `json:"width"`
	Height float64 `json:"height"`
}

// FaceMatch represents a matched face
type FaceMatch struct {
	FaceID          string  `json:"faceId"`
	ExternalImageID string  `json:"externalImageId"`
	Similarity      float64 `json:"similarity"`
	BoundingBox     BoundingBox `json:"boundingBox"`
	PhotoID         string  `json:"photoId,omitempty"`
	PhotoURL        string  `json:"photoUrl,omitempty"`
	ThumbnailURL    string  `json:"thumbnailUrl,omitempty"`
}

// NewRekognitionClient creates a new Rekognition client
func NewRekognitionClient(cfg aws.Config, collectionID string) *RekognitionClient {
	return &RekognitionClient{
		client:       rekognition.NewFromConfig(cfg),
		collectionID: collectionID,
	}
}

// CreateCollection creates a new face collection
func (r *RekognitionClient) CreateCollection(ctx context.Context, collectionName string) error {
	_, err := r.client.CreateCollection(ctx, &rekognition.CreateCollectionInput{
		CollectionId: &collectionName,
	})
	if err != nil {
		return fmt.Errorf("failed to create collection: %w", err)
	}
	return nil
}

// DeleteCollection deletes a face collection
func (r *RekognitionClient) DeleteCollection(ctx context.Context, collectionName string) error {
	_, err := r.client.DeleteCollection(ctx, &rekognition.DeleteCollectionInput{
		CollectionId: &collectionName,
	})
	if err != nil {
		return fmt.Errorf("failed to delete collection: %w", err)
	}
	return nil
}

// DetectFaces detects faces in an image
func (r *RekognitionClient) DetectFaces(ctx context.Context, imageBytes []byte) ([]FaceDetection, error) {
	input := &rekognition.DetectFacesInput{
		Image: &types.Image{
			Bytes: imageBytes,
		},
		Attributes: []types.Attribute{
			types.AttributeAll,
		},
	}

	result, err := r.client.DetectFaces(ctx, input)
	if err != nil {
		return nil, fmt.Errorf("failed to detect faces: %w", err)
	}

	var detections []FaceDetection
	for _, face := range result.FaceDetails {
		if face.BoundingBox == nil {
			continue
		}

		detection := FaceDetection{
			Confidence: float64(*face.Confidence),
			BoundingBox: BoundingBox{
				Left:   float64(*face.BoundingBox.Left),
				Top:    float64(*face.BoundingBox.Top),
				Width:  float64(*face.BoundingBox.Width),
				Height: float64(*face.BoundingBox.Height),
			},
		}
		detections = append(detections, detection)
	}

	return detections, nil
}

// IndexFace indexes a face in the collection
func (r *RekognitionClient) IndexFace(ctx context.Context, imageBytes []byte, photoID string) (*FaceDetection, error) {
	input := &rekognition.IndexFacesInput{
		CollectionId: &r.collectionID,
		Image: &types.Image{
			Bytes: imageBytes,
		},
		ExternalImageId: &photoID,
		MaxFaces:        aws.Int32(1),
		QualityFilter:   types.QualityFilterAuto,
	}

	result, err := r.client.IndexFaces(ctx, input)
	if err != nil {
		return nil, fmt.Errorf("failed to index face: %w", err)
	}

	if len(result.FaceRecords) == 0 {
		return nil, fmt.Errorf("no faces found in image")
	}

	faceRecord := result.FaceRecords[0]
	if faceRecord.FaceDetail == nil || faceRecord.FaceDetail.BoundingBox == nil {
		return nil, fmt.Errorf("no face detail available")
	}

	return &FaceDetection{
		Confidence: float64(*faceRecord.Face.Confidence),
		BoundingBox: BoundingBox{
			Left:   float64(*faceRecord.FaceDetail.BoundingBox.Left),
			Top:    float64(*faceRecord.FaceDetail.BoundingBox.Top),
			Width:  float64(*faceRecord.FaceDetail.BoundingBox.Width),
			Height: float64(*faceRecord.FaceDetail.BoundingBox.Height),
		},
	}, nil
}

// SearchFacesByImage searches for similar faces
func (r *RekognitionClient) SearchFacesByImage(ctx context.Context, imageBytes []byte, maxFaces int, threshold float64) ([]FaceMatch, error) {
	input := &rekognition.SearchFacesByImageInput{
		CollectionId: &r.collectionID,
		Image: &types.Image{
			Bytes: imageBytes,
		},
		MaxFaces:          aws.Int32(int32(maxFaces)),
		FaceMatchThreshold: aws.Float32(float32(threshold)),
	}

	result, err := r.client.SearchFacesByImage(ctx, input)
	if err != nil {
		return nil, fmt.Errorf("failed to search faces: %w", err)
	}

	var matches []FaceMatch
	for _, match := range result.FaceMatches {
		if match.Face == nil || match.Face.BoundingBox == nil {
			continue
		}

		faceMatch := FaceMatch{
			FaceID:          aws.ToString(match.Face.FaceId),
			ExternalImageID: aws.ToString(match.Face.ExternalImageId),
			Similarity:      float64(aws.ToFloat32(match.Similarity)),
			BoundingBox: BoundingBox{
				Left:   float64(*match.Face.BoundingBox.Left),
				Top:    float64(*match.Face.BoundingBox.Top),
				Width:  float64(*match.Face.BoundingBox.Width),
				Height: float64(*match.Face.BoundingBox.Height),
			},
		}
		matches = append(matches, faceMatch)
	}

	return matches, nil
}

// ExtractImageBytes extracts image bytes from a multipart file
func ExtractImageBytes(file multipart.File) ([]byte, error) {
	// Read all bytes
	data, err := io.ReadAll(file)
	if err != nil {
		return nil, fmt.Errorf("failed to read file: %w", err)
	}

	// Validate it's an image
	_, _, err = image.DecodeConfig(bytes.NewReader(data))
	if err != nil {
		return nil, fmt.Errorf("invalid image format: %w", err)
	}

	return data, nil
}

// NewRekognitionClientFromEnv creates a client from environment variables
func NewRekognitionClientFromEnv(collectionID string) (*RekognitionClient, error) {
	cfg, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	return NewRekognitionClient(cfg, collectionID), nil
}