// Package salestracking implements visits, activities, routes and location
// pings (business-rules.md §9, domain-map.md D10). Routes are role admin/sales.
// The authenticated user is resolved to their employee via an injected lookup.
package salestracking

import (
	"context"
	"time"

	"github.com/shopspring/decimal"

	"github.com/TaruLab/keluargamart-backend/internal/db"
)

// CustomerRef is the lightweight customer payload embedded on visits.
type CustomerRef struct {
	ID           int64           `json:"id"`
	Code         string          `json:"code"`
	Name         string          `json:"name"`
	CustomerType string          `json:"customer_type"`
	CreditLimit  decimal.Decimal `json:"credit_limit"`
}

// SalesVisit mirrors sales_visits (planned -> in_progress -> completed).
type SalesVisit struct {
	ID           int64        `json:"id"`
	SalesID      int64        `json:"sales_id"`
	CustomerID   int64        `json:"customer_id"`
	Customer     *CustomerRef `json:"customer,omitempty"`
	VisitDate    string       `json:"visit_date"`
	CheckInTime  *time.Time   `json:"check_in_time"`
	CheckOutTime *time.Time   `json:"check_out_time"`
	CheckInLat   *float64     `json:"check_in_lat"`
	CheckInLng   *float64     `json:"check_in_lng"`
	CheckOutLat  *float64     `json:"check_out_lat"`
	CheckOutLng  *float64     `json:"check_out_lng"`
	Purpose      string       `json:"purpose"`
	Result       string       `json:"result"`
	Notes        string       `json:"notes"`
	SalesOrderID *int64       `json:"sales_order_id"`
	Status       string       `json:"status"`
	// VisitType is 'routine' or 'billing' (jadwal kunjungan rutin vs
	// penagihan). Who may set which value is enforced in Service.CreateVisit,
	// not by this struct.
	VisitType   string `json:"visit_type"`
	ScheduledBy *int64 `json:"scheduled_by"`
	SalesName   string `json:"sales_name,omitempty"`
	PhotoCheckIn  *string `json:"photo_check_in,omitempty"`
	PhotoCheckOut *string `json:"photo_check_out,omitempty"`
}

// VisitInput mirrors the openapi VisitInput. SalesID and VisitType are only
// honored for callers with visits.create.billing/visits.create.routine —
// a plain self-service caller always gets 'routine' assigned to themself.
type VisitInput struct {
	SalesID    *int64 `json:"sales_id"`
	CustomerID int64  `json:"customer_id"`
	VisitDate  string `json:"visit_date"`
	VisitType  string `json:"visit_type"`
	Purpose    string `json:"purpose"`
	Notes      string `json:"notes"`
}

// GeoInput mirrors the openapi GeoInput (check-in/check-out GPS).
type GeoInput struct {
	Lat      *float64 `json:"lat"`
	Lng      *float64 `json:"lng"`
	Notes    string   `json:"notes"`
	Result   string   `json:"result,omitempty"`
	PhotoURL string   `json:"photo_url,omitempty"`
}

// SalesActivity mirrors sales_activities.
type SalesActivity struct {
	ID            int64   `json:"id"`
	SalesID       int64   `json:"sales_id"`
	SalesVisitID  *int64  `json:"sales_visit_id"`
	ActivityDate  string  `json:"activity_date"`
	ActivityType  string  `json:"activity_type"`
	Description   string  `json:"description"`
	Result        string  `json:"result"`
	FollowUpDate  *string `json:"follow_up_date"`
	FollowUpNotes string  `json:"follow_up_notes"`
	SalesName     string  `json:"sales_name,omitempty"`
	CustomerName  string  `json:"customer_name,omitempty"`
	Time          string  `json:"time,omitempty"`
	Notes         string  `json:"notes,omitempty"`
}

// ActivityInput mirrors the openapi ActivityInput.
type ActivityInput struct {
	SalesID       *int64  `json:"sales_id"`
	CustomerID    *int64  `json:"customer_id"`
	SalesVisitID  *int64  `json:"sales_visit_id"`
	ActivityDate  string  `json:"activity_date"`
	ActivityType  string  `json:"activity_type"`
	Description   string  `json:"description"`
	Notes         string  `json:"notes"`
	Result        string  `json:"result"`
	FollowUpDate  *string `json:"follow_up_date"`
	FollowUpNotes string  `json:"follow_up_notes"`
}

// PlanItem is one entry of a route's planned_customers JSON.
type PlanItem struct {
	CustomerID    int64   `json:"customer_id"`
	Order         int     `json:"order"`
	EstimatedTime *string `json:"estimated_time"`
}

// PlannedCustomers is the JSONB planned_customers payload.
type PlannedCustomers []PlanItem

// SalesRoute mirrors sales_routes (planned_customers stored as JSONB).
type SalesRoute struct {
	ID               int64            `json:"id"`
	SalesID          int64            `json:"sales_id"`
	RouteDate        string           `json:"route_date"`
	Name             string           `json:"name"`
	PlannedCustomers PlannedCustomers `json:"planned_customers"`
	Status           string           `json:"status"`
	Notes            string           `json:"notes"`
	SalesName        string           `json:"sales_name,omitempty"`
	LocationCount    int64            `json:"location_count,omitempty"`
}

// RouteInput mirrors the openapi RouteInput.
type RouteInput struct {
	SalesID          *int64           `json:"sales_id"`
	RouteDate        string           `json:"route_date"`
	Name             string           `json:"name"`
	PlannedCustomers PlannedCustomers `json:"planned_customers"`
	Notes            string           `json:"notes"`
}

// SalesLocation mirrors sales_locations (high-frequency GPS pings).
type SalesLocation struct {
	ID           int64      `json:"id"`
	SalesID      int64      `json:"sales_id"`
	Latitude     float64    `json:"latitude"`
	Longitude    float64    `json:"longitude"`
	Accuracy     *float64   `json:"accuracy"`
	Speed        *float64   `json:"speed"`
	Heading      *float64   `json:"heading"`
	BatteryLevel *int       `json:"battery_level"`
	RecordedAt   *time.Time `json:"recorded_at"`
}

// LocationPingInput mirrors the openapi LocationPingInput.
type LocationPingInput struct {
	Latitude     float64    `json:"latitude"`
	Longitude    float64    `json:"longitude"`
	Accuracy     *float64   `json:"accuracy"`
	Speed        *float64   `json:"speed"`
	Heading      *float64   `json:"heading"`
	BatteryLevel *int       `json:"battery_level"`
	RecordedAt   *time.Time `json:"recorded_at"`
}

// CustomerLocation is a customer site with aggregated visit counts for the
// ERP tracking "Lokasi" page. Customers are the location master;
// sales_locations holds employee GPS pings, not customer sites.
type CustomerLocation struct {
	ID         int64   `json:"id"`
	Name       string  `json:"name"`
	City       *string `json:"city"`
	VisitCount int64   `json:"visit_count"`
	IsActive   bool    `json:"is_active"`
}

// Filter carries shared list parameters.
type Filter struct {
	Page, PerPage int
	Status        string
	DateFrom      string
	DateTo        string
	Search        string
	City          string
	VisitType     string
	// Type filters activities by activity_type (visit/call/order/…).
	Type string
}

// Store is the sales-tracking persistence seam. Persistence uses the
// sqlc-generated types; the service converts rows into the API views above.
type Store interface {
	ListVisits(ctx context.Context, f Filter, salesID *int64) ([]db.ListSalesVisitsRow, int64, error)
	VisitByID(ctx context.Context, id int64) (db.GetSalesVisitByIDRow, error)
	CreateVisit(ctx context.Context, p db.CreateSalesVisitParams) (int64, error)
	UpdateVisit(ctx context.Context, p db.UpdateSalesVisitParams) error
	UpdateVisitPhotos(ctx context.Context, id int64, photoIn, photoOut *string) error
	CustomerExists(ctx context.Context, id int64) (bool, error)

	CreateActivity(ctx context.Context, p db.CreateSalesActivityParams) (int64, error)
	ListActivities(ctx context.Context, f Filter, salesID *int64) ([]db.ListSalesActivitiesRow, int64, error)

	ListRoutes(ctx context.Context, f Filter, salesID *int64) ([]db.ListSalesRoutesRow, int64, error)
	CreateRoute(ctx context.Context, p db.CreateSalesRouteParams) (int64, error)

	InsertLocations(ctx context.Context, locs []db.CreateSalesLocationParams) error
	LatestLocations(ctx context.Context) ([]db.LatestSalesLocationsRow, error)
	ListCustomerLocations(ctx context.Context, search, city string, limit, offset int) ([]CustomerLocation, int64, error)

	ListSalesEmployees(ctx context.Context) ([]db.ListSalesEmployeesRow, error)
}

// SalesEmployeeRef is the minimal {id, name} option for the Finance/SPV
// "who am I scheduling for" picker — deliberately not the full HR employee
// record (hr.employees.view is not one of Finance/SPV's grants).
type SalesEmployeeRef struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}
