package webhook

import (
	"fmt"
	"strings"
)

const (
	EventRefsChanged = "repo:refs_changed"
	MasterRefID      = "refs/heads/master"
)

// RefsChangedEvent is the Bitbucket Server repo:refs_changed webhook payload.
type RefsChangedEvent struct {
	EventKey    string     `json:"eventKey"`
	Repository  Repository `json:"repository"`
	Changes     []RefChange `json:"changes"`
	RefChanges  []RefChange `json:"refChanges"`
}

// Repository identifies a Bitbucket repository in webhook payloads.
type Repository struct {
	Slug    string  `json:"slug"`
	Project Project `json:"project"`
}

// Project holds the Bitbucket project key.
type Project struct {
	Key string `json:"key"`
}

// RefChange describes a single ref update in a push event.
type RefChange struct {
	Ref     Ref    `json:"ref"`
	RefID   string `json:"refId"`
	ToHash  string `json:"toHash"`
	FromHash string `json:"fromHash"`
	Type    string `json:"type"`
}

// Ref describes a git ref in a webhook payload.
type Ref struct {
	ID        string `json:"id"`
	DisplayID string `json:"displayId"`
	Type      string `json:"type"`
}

// RepoID returns the internal repo_id as {project_key}/{repository_slug}.
func (e RefsChangedEvent) RepoID() (string, error) {
	key := strings.TrimSpace(e.Repository.Project.Key)
	slug := strings.TrimSpace(e.Repository.Slug)
	if key == "" || slug == "" {
		return "", fmt.Errorf("repository project key and slug are required")
	}
	return key + "/" + slug, nil
}

// AllChanges returns ref changes from either changes or refChanges (Bitbucket version variants).
func (e RefsChangedEvent) AllChanges() []RefChange {
	if len(e.Changes) > 0 {
		return e.Changes
	}
	return e.RefChanges
}

// MasterChanges returns changes that affect the master branch.
func (e RefsChangedEvent) MasterChanges() []RefChange {
	var out []RefChange
	for _, change := range e.AllChanges() {
		if isMasterRef(change) {
			out = append(out, change)
		}
	}
	return out
}

func isMasterRef(change RefChange) bool {
	refID := change.Ref.ID
	if refID == "" {
		refID = change.RefID
	}
	if refID == MasterRefID {
		return true
	}
	return change.Ref.DisplayID == "master"
}
