package meta

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	"aijianzhan/platform/internal/blueprint"
)

type AppStatus string

const (
	StatusDraft        AppStatus = "draft" // 在建：已生成蓝图、尚未成功发布
	StatusValidating   AppStatus = "validating"
	StatusProvisioning AppStatus = "provisioning"
	StatusPublished    AppStatus = "published"
	StatusFailed       AppStatus = "failed"
)

// StatusLabelCN 管理端展示用中文状态。
func StatusLabelCN(s AppStatus) string {
	switch s {
	case StatusPublished:
		return "已发布"
	case StatusFailed:
		return "失败"
	case StatusDraft, StatusValidating, StatusProvisioning:
		return "在建"
	default:
		if s == "" {
			return "未知"
		}
		return string(s)
	}
}

// IsBuilding 是否视为在建（含草稿与发布中）。
func IsBuilding(s AppStatus) bool {
	return s == StatusDraft || s == StatusValidating || s == StatusProvisioning
}

type AppRecord struct {
	AppID        string               `json:"app_id"`
	TenantID     int64                `json:"tenant_id"`
	Slug         string               `json:"slug"`
	Name         string               `json:"name"`
	SchemaName   string               `json:"schema_name"`
	DatabaseName string               `json:"database_name,omitempty"`
	Engine       string               `json:"engine"`
	Status       AppStatus            `json:"status"`
	Blueprint    *blueprint.Blueprint `json:"blueprint"`
	DDL          []string             `json:"ddl,omitempty"`
	Endpoints    []string             `json:"endpoints,omitempty"`
	CreatedAt    time.Time            `json:"created_at"`
	UpdatedAt    time.Time            `json:"updated_at"`
	Error        string               `json:"error,omitempty"`
}

type ResourceRef struct {
	App      *AppRecord
	Entity   blueprint.Entity
	Resource blueprint.APIResource
}

type AppSummary struct {
	AppID        string    `json:"app_id"`
	Slug         string    `json:"slug"`
	Name         string    `json:"name"`
	Status       AppStatus `json:"status"`
	SchemaName   string    `json:"schema_name,omitempty"`
	DatabaseName string    `json:"database_name,omitempty"`
	PageCount    int       `json:"page_count"`
	EntityCount  int       `json:"entity_count"`
	UpdatedAt    time.Time `json:"updated_at"`
	CreatedAt    time.Time `json:"created_at"`
}

type Store interface {
	GetBySlug(ctx context.Context, tenantID int64, slug string) (*AppRecord, error)
	FindPublishedBySlug(ctx context.Context, slug string) (*AppRecord, error)
	ListByTenant(ctx context.Context, tenantID int64) ([]AppSummary, error)
	Save(ctx context.Context, app *AppRecord) error
	ResolveResource(ctx context.Context, tenantID int64, slug, resource string) (*ResourceRef, error)
}

type MemoryStore struct {
	mu   sync.RWMutex
	apps map[string]*AppRecord // key: tenantID:slug
}

func NewMemoryStore() *MemoryStore {
	return &MemoryStore{apps: map[string]*AppRecord{}}
}

func key(tenantID int64, slug string) string {
	return fmt.Sprintf("%d:%s", tenantID, slug)
}

func (s *MemoryStore) GetBySlug(_ context.Context, tenantID int64, slug string) (*AppRecord, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	app, ok := s.apps[key(tenantID, slug)]
	if !ok {
		return nil, fmt.Errorf("app not found")
	}
	return cloneApp(app), nil
}

func (s *MemoryStore) FindPublishedBySlug(_ context.Context, slug string) (*AppRecord, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	var best *AppRecord
	for _, app := range s.apps {
		if app == nil || app.Slug != slug || app.Status != StatusPublished {
			continue
		}
		if best == nil || app.UpdatedAt.After(best.UpdatedAt) {
			best = app
		}
	}
	if best == nil {
		return nil, fmt.Errorf("app not found")
	}
	return cloneApp(best), nil
}

func (s *MemoryStore) ListByTenant(_ context.Context, tenantID int64) ([]AppSummary, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	out := make([]AppSummary, 0)
	for _, app := range s.apps {
		if app == nil || app.TenantID != tenantID {
			continue
		}
		out = append(out, summarizeApp(app))
	}
	return out, nil
}

func (s *MemoryStore) ListPublishedApps(_ context.Context) ([]*AppRecord, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	out := make([]*AppRecord, 0)
	for _, app := range s.apps {
		if app == nil || app.Status != StatusPublished {
			continue
		}
		out = append(out, cloneApp(app))
	}
	return out, nil
}

func summarizeApp(app *AppRecord) AppSummary {
	sum := AppSummary{
		AppID:        app.AppID,
		Slug:         app.Slug,
		Name:         app.Name,
		Status:       app.Status,
		SchemaName:   app.SchemaName,
		DatabaseName: app.DatabaseName,
		UpdatedAt:    app.UpdatedAt,
		CreatedAt:    app.CreatedAt,
	}
	if app.Blueprint != nil {
		sum.PageCount = len(app.Blueprint.Pages)
		sum.EntityCount = len(app.Blueprint.Entities)
	}
	return sum
}

func (s *MemoryStore) Save(_ context.Context, app *AppRecord) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.apps[key(app.TenantID, app.Slug)] = cloneApp(app)
	return nil
}

func (s *MemoryStore) ResolveResource(ctx context.Context, tenantID int64, slug, resource string) (*ResourceRef, error) {
	app, err := s.GetBySlug(ctx, tenantID, slug)
	if err != nil {
		return nil, err
	}
	return resolveResource(app, resource)
}

func resolveResource(app *AppRecord, resource string) (*ResourceRef, error) {
	if app.Status != StatusPublished {
		return nil, fmt.Errorf("app not published")
	}
	if app.Blueprint == nil {
		return nil, fmt.Errorf("blueprint missing")
	}
	for _, r := range app.Blueprint.Apis.Resources {
		path := r.Path
		if len(path) > 0 && path[0] == '/' {
			path = path[1:]
		}
		if path != resource {
			continue
		}
		for _, e := range app.Blueprint.Entities {
			if e.Name == r.Entity {
				return &ResourceRef{App: app, Entity: e, Resource: r}, nil
			}
		}
		return nil, fmt.Errorf("entity missing for resource")
	}
	return nil, fmt.Errorf("resource not found")
}

func cloneApp(app *AppRecord) *AppRecord {
	if app == nil {
		return nil
	}
	cp := *app
	if app.Blueprint != nil {
		raw, _ := json.Marshal(app.Blueprint)
		var bp blueprint.Blueprint
		_ = json.Unmarshal(raw, &bp)
		cp.Blueprint = &bp
	}
	if app.DDL != nil {
		cp.DDL = append([]string{}, app.DDL...)
	}
	if app.Endpoints != nil {
		cp.Endpoints = append([]string{}, app.Endpoints...)
	}
	return &cp
}
