package airports

import (
	"sort"
)

// StationNeighbor represents a neighboring station that can be used as a fallback
// when the main station is closed. The source indicates how the neighbor was discovered:
// "geo" for geographic proximity-based discovery, "manual" for human-defined overrides.
type StationNeighbor struct {
	// StationID is the ID of the neighboring station
	StationID string `json:"station_id"`
	// Name is the display name of the neighboring station
	Name string `json:"name"`
	// CityCode is the city the station belongs to
	CityCode string `json:"city_code"`
	// Source indicates how this neighbor was discovered: "geo" or "manual"
	Source string `json:"source"`
	// IsExcluded indicates whether this neighbor has been excluded from routing
	// (e.g., due to closure, maintenance, or other reasons)
	IsExcluded bool `json:"is_excluded"`
}

// StationNeighbors manages a collection of station neighbors for a given city.
// It supports both geo-discovered and manually-defined neighbors.
type StationNeighbors struct {
	// CityCode is the city these neighbors belong to
	CityCode string
	// Neighbors is the list of neighboring stations
	Neighbors []StationNeighbor
	// byID maps station ID to index in Neighbors for quick lookup
	byID map[string]int
}

// NewStationNeighbors creates a new StationNeighbors instance for the given city code.
func NewStationNeighbors(cityCode string) *StationNeighbors {
	return &StationNeighbors{
		CityCode:  cityCode,
		Neighbors: []StationNeighbor{},
		byID:      make(map[string]int),
	}
}

// Add adds a neighbor to the collection.
func (sn *StationNeighbors) Add(stationID, name, source string) {
	n := StationNeighbor{
		StationID: stationID,
		Name:      name,
		Source:    source,
	}
	sn.Neighbors = append(sn.Neighbors, n)
	sn.byID[stationID] = len(sn.Neighbors) - 1
}

// MarkExcluded marks a neighbor as excluded from routing.
func (sn *StationNeighbors) MarkExcluded(stationID string) {
	if idx, ok := sn.byID[stationID]; ok {
		sn.Neighbors[idx].IsExcluded = true
	}
}

// IsExcluded returns whether a neighbor with the given station ID is excluded.
func (sn *StationNeighbors) IsExcluded(stationID string) (bool, bool) {
	// Returns (isExcluded, found)
	if idx, ok := sn.byID[stationID]; ok {
		return sn.Neighbors[idx].IsExcluded, true
	}
	return false, false
}

// Get returns a neighbor by station ID.
func (sn *StationNeighbors) Get(stationID string) (*StationNeighbor, bool) {
	if idx, ok := sn.byID[stationID]; ok {
		return &sn.Neighbors[idx], true
	}
	return nil, false
}

// Len returns the number of neighbors.
func (sn *StationNeighbors) Len() int {
	return len(sn.Neighbors)
}

// Sort sorts neighbors by name.
func (sn *StationNeighbors) Sort() {
	sort.Slice(sn.Neighbors, func(i, j int) bool {
		return sn.Neighbors[i].Name < sn.Neighbors[j].Name
	})
}

// GetNonExcluded returns neighbors that are not excluded.
func (sn *StationNeighbors) GetNonExcluded() []StationNeighbor {
	var result []StationNeighbor
	for _, n := range sn.Neighbors {
		if !n.IsExcluded {
			result = append(result, n)
		}
	}
	return result
}
