package core

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/Yenn503/NecropolisC2/pkg/cryptography"
)

// colorLogWriter wraps a writer and colorizes known log prefixes.
type colorLogWriter struct{ w io.Writer }

func (c *colorLogWriter) Write(p []byte) (int, error) {
	s := string(p)
	switch {
	case strings.Contains(s, "[operator] "):
		s = strings.Replace(s, "[operator] ", boldCyan("[operator] "), 1)
	case strings.Contains(s, "[implant] "):
		s = strings.Replace(s, "[implant] ", boldGreen("[implant] "), 1)
	case strings.Contains(s, "[evasion] "):
		s = strings.Replace(s, "[evasion] ", magentaBold("[evasion] "), 1)
	case strings.Contains(s, "[socks] "):
		s = strings.Replace(s, "[socks] ", dimYellow("[socks] "), 1)
	case strings.Contains(s, "[node] "):
		s = strings.Replace(s, "[node] ", dim+"[node]"+reset+" ", 1)
	case strings.Contains(s, "[WARN] "):
		s = strings.Replace(s, "[WARN]", boldRed("[WARN]"), 1)
	case strings.Contains(s, "DISCONNECTED"):
		s = strings.Replace(s, "DISCONNECTED", boldRed("DISCONNECTED"), 1)
	}
	return c.w.Write([]byte(s))
}

func Run(relayAddrs []string) error {
	log.SetOutput(&colorLogWriter{w: os.Stderr})
	if err := os.MkdirAll(necropolisDir(), 0700); err != nil {
		return fmt.Errorf("create necropolis directory: %w", err)
	}

	keys, err := cryptography.LoadOrGenerateOperatorKey(keyPath())
	if err != nil {
		return fmt.Errorf("load operator key: %w", err)
	}

	pubPath := pubKeyPath()
	if _, err := os.Stat(pubPath); os.IsNotExist(err) {
		pubBytes, err := crypto.MarshalPublicKey(keys.PublicKey)
		if err == nil {
			if err := os.WriteFile(pubPath, pubBytes, 0644); err == nil {
				log.Printf("[operator] public key exported: %s", pubPath)
			}
		}
	}

	log.Printf("[operator] peer ID: %s", keys.PeerID.String())

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	op, err := NewOperator(ctx, keys, relayAddrs)
	if err != nil {
		return fmt.Errorf("create operator: %w", err)
	}
	defer op.Close()

	if err := op.Start(); err != nil {
		return fmt.Errorf("start operator: %w", err)
	}

	op.RunCLI()
	return nil
}

func necropolisDir() string {
	home, err := os.UserHomeDir()
	if err != nil {
		return ".necropolis"
	}
	return filepath.Join(home, ".necropolis")
}

func keyPath() string {
	return filepath.Join(necropolisDir(), "operator.key")
}

func pubKeyPath() string {
	return filepath.Join(necropolisDir(), "operator.pub")
}
