package adapters

import json "core:encoding/json"
import "core:fmt"
import "core:os"
import filepath "core:path/filepath"
import "../core"
import "../shared"

CURRENT_PROJECT_SCHEMA_VERSION :: 1

Comic_Project_Document :: struct {
	schemaVersion: int,
	assetCacheDir: string,
	state: core.Comic_State,
}

join_paths :: proc(a, b: string) -> (string, shared.App_Error) {
	parts := [2]string{a, b}
	joined, err := filepath.join(parts[:], context.temp_allocator)
	if err != nil {
		return "", shared.new_error(.Storage, fmt.aprintf("path join failed: %v", err), false)
	}
	return joined, shared.ok()
}

derive_asset_cache_dir :: proc(project_file_path: string) -> (string, shared.App_Error) {
	dir, _ := filepath.split(project_file_path)
	if len(dir) == 0 {
		dir = "."
	}
	return join_paths(dir, "assets")
}

ensure_project_layout :: proc(project_file_path: string) -> shared.App_Error {
	dir, _ := filepath.split(project_file_path)
	if len(dir) > 0 {
		if err := os.mkdir_all(dir); err != nil && err != .Exist {
			return shared.new_error(.Storage, fmt.aprintf("failed to create project directory: %v", err), true)
		}
	}

	asset_dir, aerr := derive_asset_cache_dir(project_file_path)
	if !shared.is_ok(aerr) {
		return aerr
	}
	if err := os.mkdir_all(asset_dir); err != nil && err != .Exist {
		return shared.new_error(.Storage, fmt.aprintf("failed to create asset cache directory: %v", err), true)
	}

	return shared.ok()
}

migrate_document_to_current_state :: proc(doc: Comic_Project_Document) -> (core.Comic_State, shared.App_Error) {
	switch doc.schemaVersion {
	case 1:
		return doc.state, shared.ok()
	case:
		return core.Comic_State{}, shared.new_error(.Storage, fmt.aprintf("unsupported schemaVersion: %d", doc.schemaVersion), false)
	}
}

save_project :: proc(path: string, state: core.Comic_State) -> shared.App_Error {
	if len(path) == 0 {
		return shared.new_error(.Storage, "save path is empty", false)
	}

	if lerr := ensure_project_layout(path); !shared.is_ok(lerr) {
		return lerr
	}

	asset_dir, derr := derive_asset_cache_dir(path)
	if !shared.is_ok(derr) {
		return derr
	}

	doc := Comic_Project_Document{
		schemaVersion = CURRENT_PROJECT_SCHEMA_VERSION,
		assetCacheDir = asset_dir,
		state = state,
	}

	payload, merr := json.marshal(doc, json.Marshal_Options{pretty = true, use_spaces = true, spaces = 2}, context.temp_allocator)
	if merr != nil {
		return shared.new_error(.Storage, fmt.aprintf("failed to marshal project: %v", merr), false)
	}

	if werr := os.write_entire_file(path, payload); werr != nil {
		return shared.new_error(.Storage, fmt.aprintf("failed to write project file: %v", werr), true)
	}

	return shared.ok()
}

load_project :: proc(path: string) -> (core.Comic_State, shared.App_Error) {
	if len(path) == 0 {
		return core.Comic_State{}, shared.new_error(.Storage, "load path is empty", false)
	}
	if !os.exists(path) {
		return core.Comic_State{}, shared.new_error(.Storage, "project file does not exist", false)
	}

	payload, rerr := os.read_entire_file(path, context.temp_allocator)
	if rerr != nil {
		return core.Comic_State{}, shared.new_error(.Storage, fmt.aprintf("failed to read project file: %v", rerr), true)
	}

	doc: Comic_Project_Document
	if err := json.unmarshal(payload, &doc); err == nil {
		if doc.schemaVersion <= 0 {
			return core.Comic_State{}, shared.new_error(.Storage, "invalid project schemaVersion", false)
		}
		state, merr := migrate_document_to_current_state(doc)
		delete(doc.assetCacheDir)
		return state, merr
	}

	// Legacy fallback: raw Comic_State payload without wrapper
	legacy: core.Comic_State
	if err := json.unmarshal(payload, &legacy); err == nil {
		return legacy, shared.ok()
	}

	return core.Comic_State{}, shared.new_error(.Storage, "failed to decode project payload", false)
}

save_project_stub :: proc(path: string, state: core.Comic_State) -> shared.App_Error {
	return save_project(path, state)
}

load_project_stub :: proc(path: string) -> (core.Comic_State, shared.App_Error) {
	return load_project(path)
}
