package keeper

import (
	"context"
	"time"

	"github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"

	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"

	"cosmossdk.io/collections"
	storetypes "cosmossdk.io/core/store"
	"cosmossdk.io/log"
	"cosmossdk.io/orm/model/ormdb"

	"github.com/ipfs/boxo/path"
	"github.com/ipfs/kubo/client/rpc"
	apiv1 "github.com/onsonr/sonr/api/vault/v1"

	"github.com/onsonr/sonr/x/vault/types"

	didkeeper "github.com/onsonr/sonr/x/did/keeper"
)

type Keeper struct {
	cdc codec.BinaryCodec

	logger log.Logger

	// state management
	Schema collections.Schema
	Params collections.Item[types.Params]
	OrmDB  apiv1.StateStore

	authority string

	ipfsClient *rpc.HttpApi

	DIDKeeper didkeeper.Keeper
}

// NewKeeper creates a new Keeper instance
func NewKeeper(
	cdc codec.BinaryCodec,
	storeService storetypes.KVStoreService,
	logger log.Logger,
	authority string,
	didk didkeeper.Keeper,
) Keeper {
	logger = logger.With(log.ModuleKey, "x/"+types.ModuleName)

	sb := collections.NewSchemaBuilder(storeService)

	if authority == "" {
		authority = authtypes.NewModuleAddress(govtypes.ModuleName).String()
	}

	db, err := ormdb.NewModuleDB(&types.ORMModuleSchema, ormdb.ModuleDBOptions{KVStoreService: storeService})
	if err != nil {
		panic(err)
	}

	store, err := apiv1.NewStateStore(db)
	if err != nil {
		panic(err)
	}

	ipfsClient, _ := rpc.NewLocalApi()
	k := Keeper{
		cdc:       cdc,
		logger:    logger,
		DIDKeeper: didk,
		Params:    collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
		OrmDB:     store,

		ipfsClient: ipfsClient,
		authority:  authority,
	}

	schema, err := sb.Build()
	if err != nil {
		panic(err)
	}

	k.Schema = schema

	return k
}

func (k Keeper) Logger() log.Logger {
	return k.logger
}

// InitGenesis initializes the module's state from a genesis state.
func (k *Keeper) InitGenesis(ctx context.Context, data *types.GenesisState) error {
	// this line is used by starport scaffolding # genesis/module/init
	if err := data.Params.Validate(); err != nil {
		return err
	}

	return k.Params.Set(ctx, data.Params)
}

// ExportGenesis exports the module's state to a genesis state.
func (k *Keeper) ExportGenesis(ctx context.Context) *types.GenesisState {
	params, err := k.Params.Get(ctx)
	if err != nil {
		panic(err)
	}

	// this line is used by starport scaffolding # genesis/module/export

	return &types.GenesisState{
		Params: params,
	}
}

// IPFSConnected returns true if the IPFS client is initialized
func (c Keeper) IPFSConnected() bool {
	if c.ipfsClient == nil {
		ipfsClient, err := rpc.NewLocalApi()
		if err != nil {
			return false
		}
		c.ipfsClient = ipfsClient
	}
	return c.ipfsClient != nil
}

// CalculateExpiration calculates the expiration time for a vault
func (k Keeper) CalculateExpiration(c sdk.Context, duration time.Duration) int64 {
	blockTime := c.BlockTime()
	avgBlockTime := float64(blockTime.Sub(blockTime).Seconds())
	return int64(duration.Seconds() / avgBlockTime)
}

// HasPathInIPFS checks if a file is in the local IPFS node
func (k Keeper) HasPathInIPFS(ctx sdk.Context, cid string) (bool, error) {
	path, err := path.NewPath(cid)
	if err != nil {
		return false, err
	}
	v, err := k.ipfsClient.Unixfs().Get(ctx, path)
	if err != nil {
		return false, err
	}

	if v == nil {
		return false, nil
	}
	return true, nil
}
