package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"os/signal"
	"syscall"
	"time"

	"github.com/example/storage-appliance/internal/audit"
	httpin "github.com/example/storage-appliance/internal/http"
	iscsiinfra "github.com/example/storage-appliance/internal/infra/iscsi"
	"github.com/example/storage-appliance/internal/infra/nfs"
	"github.com/example/storage-appliance/internal/infra/osexec"
	"github.com/example/storage-appliance/internal/infra/samba"
	"github.com/example/storage-appliance/internal/infra/sqlite/db"
	"github.com/example/storage-appliance/internal/infra/zfs"
	"github.com/example/storage-appliance/internal/job"
	iscsiSvcPkg "github.com/example/storage-appliance/internal/service/iscsi"
	"github.com/example/storage-appliance/internal/service/mock"
	"github.com/example/storage-appliance/internal/service/shares"
	"github.com/example/storage-appliance/internal/service/storage"
	_ "github.com/glebarez/sqlite"
	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"
)

func main() {
	ctx := context.Background()
	
	// Determine data directory (use /opt/adastra-storage/data in production, current dir in dev)
	dataDir := os.Getenv("DATA_DIR")
	if dataDir == "" {
		dataDir = os.Getenv("INSTALL_DIR")
		if dataDir != "" {
			dataDir = filepath.Join(dataDir, "data")
		} else {
			dataDir = "." // Development mode
		}
	}
	
	// Ensure data directory exists
	if err := os.MkdirAll(dataDir, 0755); err != nil {
		log.Fatalf("failed to create data directory: %v", err)
	}
	
	// Connect simple sqlite DB (file)
	dbPath := filepath.Join(dataDir, "appliance.db")
	dsn := fmt.Sprintf("file:%s?_foreign_keys=on", dbPath)
	sqldb, err := sql.Open("sqlite", dsn)
	if err != nil {
		log.Fatalf("open db: %v", err)
	}
	defer sqldb.Close()

	// Run migrations and seed admin
	if err := db.MigrateAndSeed(ctx, sqldb); err != nil {
		log.Fatalf("migrate: %v", err)
	}

	r := chi.NewRouter()
	uuidMiddleware := func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			rid := uuid.New().String()
			rw := w
			r = r.WithContext(context.WithValue(r.Context(), httpin.ContextKeyRequestID, rid))
			rw.Header().Set("X-Request-Id", rid)
			next.ServeHTTP(rw, r)
		})
	}

	// Attach router and app dependencies
	// wire mocks for now; replace with real adapters in infra
	diskSvc := &mock.MockDiskService{}
	// job runner uses sqlite DB and zfs adapter
	zfsAdapter := zfs.NewAdapter(osexec.Default)
	jobRunner := &job.Runner{DB: sqldb}
	auditLogger := audit.NewSQLAuditLogger(sqldb)
	jobRunner.ZFS = zfsAdapter
	jobRunner.Audit = auditLogger
	// storage service wiring: use zfsAdapter and jobRunner and audit logger
	storageSvc := storage.NewStorageService(zfsAdapter, jobRunner, auditLogger)
	nfsAdapter := nfs.NewAdapter(osexec.Default, "")
	sambaAdapter := samba.NewAdapter(osexec.Default, "")
	sharesSvc := shares.NewSharesService(sqldb, nfsAdapter, sambaAdapter, auditLogger)

	// iSCSI adapter and service
	iscsiAdapter := iscsiinfra.NewAdapter(osexec.Default)
	iscsiSvc := iscsiSvcPkg.NewISCSIService(sqldb, zfsAdapter, iscsiAdapter, auditLogger)

	zfsSvc := zfsAdapter
	app := &httpin.App{
		DB:         sqldb,
		DiskSvc:    diskSvc,
		ZFSSvc:     zfsSvc,
		JobRunner:  jobRunner,
		HTTPClient: &http.Client{},
		StorageSvc: storageSvc,
		ShareSvc:   sharesSvc,
		ISCSISvc:   iscsiSvc,
		Runner:     osexec.Default,
	}
	r.Use(uuidMiddleware)
	httpin.RegisterRoutes(r, app)

	srv := &http.Server{
		Addr:    ":8080",
		Handler: r,
	}

	// graceful shutdown
	go func() {
		log.Printf("Starting server on %s", srv.Addr)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("server error: %v", err)
		}
	}()

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	<-stop

	ctxShut, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()
	if err := srv.Shutdown(ctxShut); err != nil {
		log.Fatalf("server shutdown failed: %v", err)
	}
	log.Println("server stopped")
}
