package dbsync

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/google/uuid"
)

// Binding 本机库 ↔ 线上库映射（登记/查询；用户自助 push 时按 user_id+online_db_id 鉴权）。
type Binding struct {
	ID              string    `json:"id"`
	TenantID        int64     `json:"tenant_id"`
	UserID          int64     `json:"user_id,omitempty"`
	LocalDatabaseID string    `json:"local_database_id"`
	OnlineDBID      string    `json:"online_db_id"`
	ChannelID       string    `json:"channel_id,omitempty"`
	// Shared：公司共享库（Z12c-2）；同租户任意成员可 push/pull 该 online_db_id
	Shared bool `json:"shared,omitempty"`
	// 可读名（与宇恒 database_name / display_name 对齐；控制台优先展示）
	DatabaseName string `json:"database_name,omitempty"` // 本地库可读名
	DisplayName  string `json:"display_name,omitempty"`  // 线上库可读名
	Note         string    `json:"note,omitempty"`
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
}

func (s *FileStore) bindingPath() string {
	return filepath.Join(s.dir, "bindings.json")
}

func (s *FileStore) EnsureBinding(b Binding) (Binding, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	list, err := s.readBindingsUnlocked()
	if err != nil {
		return b, err
	}
	b.LocalDatabaseID = strings.TrimSpace(b.LocalDatabaseID)
	b.OnlineDBID = strings.TrimSpace(b.OnlineDBID)
	b.DatabaseName = strings.TrimSpace(b.DatabaseName)
	b.DisplayName = strings.TrimSpace(b.DisplayName)
	if b.TenantID <= 0 {
		return b, fmt.Errorf("tenant_id required")
	}
	if b.LocalDatabaseID == "" || b.OnlineDBID == "" {
		return b, fmt.Errorf("local_database_id and online_db_id required")
	}
	now := time.Now().UTC()
	for i := range list {
		if list[i].TenantID == b.TenantID && list[i].LocalDatabaseID == b.LocalDatabaseID {
			if b.UserID > 0 && list[i].UserID > 0 && list[i].UserID != b.UserID {
				continue
			}
			list[i].OnlineDBID = b.OnlineDBID
			if b.ChannelID != "" {
				list[i].ChannelID = b.ChannelID
			}
			if b.DatabaseName != "" {
				list[i].DatabaseName = b.DatabaseName
			}
			if b.DisplayName != "" {
				list[i].DisplayName = b.DisplayName
			}
			if b.Note != "" {
				list[i].Note = b.Note
			}
			if b.UserID > 0 {
				list[i].UserID = b.UserID
			}
			list[i].Shared = b.Shared
			list[i].UpdatedAt = now
			if err := s.writeBindingsUnlocked(list); err != nil {
				return b, err
			}
			return list[i], nil
		}
	}
	if b.ID == "" {
		b.ID = uuid.NewString()
	}
	b.CreatedAt = now
	b.UpdatedAt = now
	list = append(list, b)
	if err := s.writeBindingsUnlocked(list); err != nil {
		return b, err
	}
	return b, nil
}

func (s *FileStore) ListBindings(tenantID int64, localDatabaseID string) ([]Binding, error) {
	return s.ListBindingsFiltered(tenantID, 0, localDatabaseID)
}

// ListBindingsFiltered 按租户列出；userID>0 时仅返回该用户的 Binding。
func (s *FileStore) ListBindingsFiltered(tenantID, userID int64, localDatabaseID string) ([]Binding, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	list, err := s.readBindingsUnlocked()
	if err != nil {
		return nil, err
	}
	out := make([]Binding, 0)
	for _, b := range list {
		if b.TenantID != tenantID {
			continue
		}
		if userID > 0 && b.UserID != userID {
			continue
		}
		if localDatabaseID != "" && b.LocalDatabaseID != localDatabaseID {
			continue
		}
		out = append(out, b)
	}
	return out, nil
}

// UserOwnsOnlineDB 用户是否登记了该 online_db_id（可选限定 channel）；或该库为公司 Shared。
func (s *FileStore) UserOwnsOnlineDB(tenantID, userID int64, channelID, onlineDBID string) bool {
	if tenantID <= 0 || userID <= 0 || strings.TrimSpace(onlineDBID) == "" {
		return false
	}
	online := strings.TrimSpace(onlineDBID)
	ch := strings.TrimSpace(channelID)
	own, err := s.ListBindingsFiltered(tenantID, userID, "")
	if err == nil {
		for _, b := range own {
			if strings.TrimSpace(b.OnlineDBID) != online {
				continue
			}
			if ch != "" && b.ChannelID != "" && b.ChannelID != ch {
				continue
			}
			return true
		}
	}
	all, err := s.ListBindingsFiltered(tenantID, 0, "")
	if err != nil {
		return false
	}
	for _, b := range all {
		if !b.Shared {
			continue
		}
		if strings.TrimSpace(b.OnlineDBID) != online {
			continue
		}
		if ch != "" && b.ChannelID != "" && b.ChannelID != ch {
			continue
		}
		return true
	}
	return false
}

// UserCanAccessChannel 用户是否登记了指向该通道的 Binding（channel_id 空视为未限定通道）；含 Shared。
func (s *FileStore) UserCanAccessChannel(tenantID, userID int64, channelID string) bool {
	if tenantID <= 0 || userID <= 0 || strings.TrimSpace(channelID) == "" {
		return false
	}
	ch := strings.TrimSpace(channelID)
	list, err := s.ListBindingsFiltered(tenantID, userID, "")
	if err == nil {
		for _, b := range list {
			if b.ChannelID == "" || b.ChannelID == ch {
				return true
			}
		}
	}
	all, err := s.ListBindingsFiltered(tenantID, 0, "")
	if err != nil {
		return false
	}
	for _, b := range all {
		if !b.Shared {
			continue
		}
		if b.ChannelID == "" || b.ChannelID == ch {
			return true
		}
	}
	return false
}

func (s *FileStore) GetBinding(tenantID int64, localDatabaseID string) (*Binding, error) {
	list, err := s.ListBindings(tenantID, localDatabaseID)
	if err != nil {
		return nil, err
	}
	if len(list) == 0 {
		return nil, fmt.Errorf("binding not found")
	}
	cp := list[0]
	return &cp, nil
}

func (s *FileStore) readBindingsUnlocked() ([]Binding, error) {
	path := s.bindingPath()
	b, err := os.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) {
			return []Binding{}, nil
		}
		return nil, err
	}
	if len(b) == 0 {
		return []Binding{}, nil
	}
	var list []Binding
	if err := json.Unmarshal(b, &list); err != nil {
		return nil, err
	}
	return list, nil
}

func (s *FileStore) writeBindingsUnlocked(list []Binding) error {
	path := s.bindingPath()
	raw, err := json.MarshalIndent(list, "", "  ")
	if err != nil {
		return err
	}
	tmp := path + ".tmp"
	if err := os.WriteFile(tmp, raw, 0o600); err != nil {
		return err
	}
	return os.Rename(tmp, path)
}
