// Package mospkg provides functionality for Mosis app packages (.mosis files)
package mospkg

import (
	"encoding/json"
	"fmt"
	"regexp"
)

// Manifest represents the app manifest (manifest.json)
type Manifest struct {
	Schema            string   `json:"$schema,omitempty"`
	ID                string   `json:"id"`
	Name              string   `json:"name"`
	Version           string   `json:"version"`
	VersionCode       int      `json:"version_code"`
	Entry             string   `json:"entry"`
	MinMosisVersion   string   `json:"min_mosis_version"`
	Description       string   `json:"description,omitempty"`
	Author            *Author  `json:"author,omitempty"`
	Permissions       []string `json:"permissions,omitempty"`
	Icons             Icons    `json:"icons,omitempty"`
	TargetMosisVer    string   `json:"target_mosis_version,omitempty"`
	Category          string   `json:"category,omitempty"`
	Tags              []string `json:"tags,omitempty"`
	Orientation       string   `json:"orientation,omitempty"`
	BackgroundColor   string   `json:"background_color,omitempty"`
	Locales           []string `json:"locales,omitempty"`
	DefaultLocale     string   `json:"default_locale,omitempty"`
}

// Author represents the app author information
type Author struct {
	Name  string `json:"name"`
	Email string `json:"email,omitempty"`
	URL   string `json:"url,omitempty"`
}

// Icons represents app icon paths by size
type Icons struct {
	Size32  string `json:"32,omitempty"`
	Size64  string `json:"64,omitempty"`
	Size128 string `json:"128,omitempty"`
}

// ParseManifest parses a manifest.json from bytes
func ParseManifest(data []byte) (*Manifest, error) {
	var m Manifest
	if err := json.Unmarshal(data, &m); err != nil {
		return nil, fmt.Errorf("parse manifest: %w", err)
	}
	return &m, nil
}

// Validate checks if the manifest contains all required fields with valid values
func (m *Manifest) Validate() []ValidationError {
	var errors []ValidationError

	// Required fields
	if m.ID == "" {
		errors = append(errors, ValidationError{
			Code:    "MISSING_ID",
			Message: "Manifest is missing required field: id",
		})
	} else if !isValidPackageID(m.ID) {
		errors = append(errors, ValidationError{
			Code:    "INVALID_ID",
			Message: "Package ID must be in reverse domain format (e.g., com.developer.app)",
			File:    "manifest.json",
		})
	}

	if m.Name == "" {
		errors = append(errors, ValidationError{
			Code:    "MISSING_NAME",
			Message: "Manifest is missing required field: name",
		})
	} else if len(m.Name) > 30 {
		errors = append(errors, ValidationError{
			Code:    "NAME_TOO_LONG",
			Message: "App name must be 30 characters or less",
		})
	}

	if m.Version == "" {
		errors = append(errors, ValidationError{
			Code:    "MISSING_VERSION",
			Message: "Manifest is missing required field: version",
		})
	} else if !isValidSemver(m.Version) {
		errors = append(errors, ValidationError{
			Code:    "INVALID_VERSION",
			Message: "Version must be in semantic version format (X.Y.Z)",
		})
	}

	if m.VersionCode <= 0 {
		errors = append(errors, ValidationError{
			Code:    "INVALID_VERSION_CODE",
			Message: "version_code must be a positive integer",
		})
	}

	if m.Entry == "" {
		errors = append(errors, ValidationError{
			Code:    "MISSING_ENTRY",
			Message: "Manifest is missing required field: entry",
		})
	}

	if m.MinMosisVersion == "" {
		errors = append(errors, ValidationError{
			Code:    "MISSING_MIN_MOSIS_VERSION",
			Message: "Manifest is missing required field: min_mosis_version",
		})
	}

	// Validate permissions
	for _, perm := range m.Permissions {
		if !isValidPermission(perm) {
			errors = append(errors, ValidationError{
				Code:    "INVALID_PERMISSION",
				Message: fmt.Sprintf("Unknown permission: %s", perm),
			})
		}
	}

	// Validate orientation if specified
	if m.Orientation != "" {
		validOrientations := map[string]bool{"portrait": true, "landscape": true, "any": true}
		if !validOrientations[m.Orientation] {
			errors = append(errors, ValidationError{
				Code:    "INVALID_ORIENTATION",
				Message: "Orientation must be one of: portrait, landscape, any",
			})
		}
	}

	// Validate background color if specified
	if m.BackgroundColor != "" && !isValidHexColor(m.BackgroundColor) {
		errors = append(errors, ValidationError{
			Code:    "INVALID_BACKGROUND_COLOR",
			Message: "background_color must be a valid hex color (e.g., #FFFFFF)",
		})
	}

	return errors
}

// Package ID pattern: reverse domain notation
var packageIDPattern = regexp.MustCompile(`^[a-z][a-z0-9]*(\.[a-z][a-z0-9_]*)+$`)

func isValidPackageID(id string) bool {
	return packageIDPattern.MatchString(id)
}

// Semver pattern: X.Y.Z
var semverPattern = regexp.MustCompile(`^\d+\.\d+\.\d+$`)

func isValidSemver(version string) bool {
	return semverPattern.MatchString(version)
}

// Hex color pattern: #RGB or #RRGGBB
var hexColorPattern = regexp.MustCompile(`^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$`)

func isValidHexColor(color string) bool {
	return hexColorPattern.MatchString(color)
}

// Valid permissions
var validPermissions = map[string]bool{
	"storage":    true,
	"network":    true,
	"camera":     true,
	"microphone": true,
	"location":   true,
	"contacts":   true,
	"calendar":   true,
	"sensors":    true,
}

func isValidPermission(perm string) bool {
	return validPermissions[perm]
}
