package nostrfs

import (
	"bytes"
	"context"
	"encoding/binary"
	"encoding/json"
	"io"
	"net/http"
	"sync/atomic"
	"syscall"
	"time"

	"fiatjaf.com/nostr"
	"fiatjaf.com/nostr/nip19"
	"github.com/fatih/color"
	"github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"
	"github.com/liamg/magic"
)

type NpubDir struct {
	fs.Inode
	root    *NostrRoot
	pointer nostr.ProfilePointer
	fetched atomic.Bool
}

var _ = (fs.NodeOnAdder)((*NpubDir)(nil))

func (r *NostrRoot) CreateNpubDir(
	parent fs.InodeEmbedder,
	pointer nostr.ProfilePointer,
	signer nostr.Signer,
) *fs.Inode {
	npubdir := &NpubDir{root: r, pointer: pointer}
	return parent.EmbeddedInode().NewPersistentInode(
		r.ctx,
		npubdir,
		fs.StableAttr{Mode: syscall.S_IFDIR, Ino: binary.BigEndian.Uint64(pointer.PublicKey[8:16])},
	)
}

func (h *NpubDir) OnAdd(_ context.Context) {
	log := h.root.ctx.Value("log").(func(msg string, args ...any))

	relays := h.root.sys.FetchOutboxRelays(h.root.ctx, h.pointer.PublicKey, 2)
	log("- adding folder for %s with relays %s\n",
		color.HiYellowString(nip19.EncodePointer(h.pointer)), color.HiGreenString("%v", relays))

	h.AddChild("pubkey", h.NewPersistentInode(
		h.root.ctx,
		&fs.MemRegularFile{Data: []byte(h.pointer.PublicKey.Hex() + "\n"), Attr: fuse.Attr{Mode: 0444}},
		fs.StableAttr{},
	), true)

	go func() {
		pm := h.root.sys.FetchProfileMetadata(h.root.ctx, h.pointer.PublicKey)
		if pm.Event == nil {
			return
		}

		metadataj, _ := json.MarshalIndent(pm, "", "  ")
		h.AddChild(
			"metadata.json",
			h.NewPersistentInode(
				h.root.ctx,
				&fs.MemRegularFile{
					Data: metadataj,
					Attr: fuse.Attr{
						Mtime: uint64(pm.Event.CreatedAt),
						Mode:  0444,
					},
				},
				fs.StableAttr{},
			),
			true,
		)

		ctx, cancel := context.WithTimeout(h.root.ctx, time.Second*20)
		defer cancel()
		req, err := http.NewRequestWithContext(ctx, "GET", pm.Picture, nil)
		if err == nil {
			resp, err := http.DefaultClient.Do(req)
			if err == nil {
				defer resp.Body.Close()
				if resp.StatusCode < 300 {
					b := &bytes.Buffer{}
					io.Copy(b, resp.Body)

					ext := "png"
					if ft, err := magic.Lookup(b.Bytes()); err == nil {
						ext = ft.Extension
					}

					h.AddChild("picture."+ext, h.NewPersistentInode(
						ctx,
						&fs.MemRegularFile{
							Data: b.Bytes(),
							Attr: fuse.Attr{
								Mtime: uint64(pm.Event.CreatedAt),
								Mode:  0444,
							},
						},
						fs.StableAttr{},
					), true)
				}
			}
		}
	}()

	if h.GetChild("notes") == nil {
		h.AddChild(
			"notes",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{1},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    true,
					relays:      relays,
					replaceable: false,
					createable:  true,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}

	if h.GetChild("comments") == nil {
		h.AddChild(
			"comments",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{1111},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    true,
					relays:      relays,
					replaceable: false,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}

	if h.GetChild("photos") == nil {
		h.AddChild(
			"photos",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{20},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    true,
					relays:      relays,
					replaceable: false,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}

	if h.GetChild("videos") == nil {
		h.AddChild(
			"videos",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{21, 22},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    false,
					relays:      relays,
					replaceable: false,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}

	if h.GetChild("highlights") == nil {
		h.AddChild(
			"highlights",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{9802},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    false,
					relays:      relays,
					replaceable: false,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}

	if h.GetChild("articles") == nil {
		h.AddChild(
			"articles",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{30023},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    false,
					relays:      relays,
					replaceable: true,
					createable:  true,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}

	if h.GetChild("wiki") == nil {
		h.AddChild(
			"wiki",
			h.NewPersistentInode(
				h.root.ctx,
				&ViewDir{
					root: h.root,
					filter: nostr.Filter{
						Kinds:   []nostr.Kind{30818},
						Authors: []nostr.PubKey{h.pointer.PublicKey},
					},
					paginate:    false,
					relays:      relays,
					replaceable: true,
					createable:  true,
				},
				fs.StableAttr{Mode: syscall.S_IFDIR},
			),
			true,
		)
	}
}