package laravel

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

// DefineTableColumn describes a single column the agent wants to persist.
// Nullable and Default are optional; they are omitted from the JSON body when
// unset so Laravel's `sometimes` validation does not see null/empty values.
//
// SECURITY: this struct intentionally carries NO secret_key / db_connection —
// those are resolved server-side in Laravel and must never be sent by the
// builder or exposed to the agent.
type DefineTableColumn struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Nullable *bool  `json:"nullable,omitempty"`
	Default  string `json:"default,omitempty"`
}

// DefineTableSpec is the structured table definition the agent declares.
type DefineTableSpec struct {
	Table   string              `json:"table"`
	Columns []DefineTableColumn `json:"columns"`
	Access  string              `json:"access"` // "public" | "private"
}

// DefineTableResult is the JSON response from the Laravel define-table endpoint.
type DefineTableResult struct {
	OK     bool   `json:"ok"`
	Error  string `json:"error,omitempty"`
	Table  string `json:"table,omitempty"`
	Access string `json:"access,omitempty"`
}

// DefineTable asks Laravel to create a table (with platform-authored SQL + RLS)
// for the project mapped to the given builder session id. The agent never
// authors SQL — it only declares the structured spec.
//
// A non-nil error indicates a transport/HTTP failure (the agent should retry).
// A nil error with res.OK == false indicates Laravel rejected the spec
// (validation, unknown session, capability disabled) — the agent should read
// res.Error and correct itself.
func (c *Client) DefineTable(sessionID string, spec DefineTableSpec) (DefineTableResult, error) {
	payload := struct {
		SessionID string              `json:"session_id"`
		Table     string              `json:"table"`
		Columns   []DefineTableColumn `json:"columns"`
		Access    string              `json:"access"`
	}{
		SessionID: sessionID,
		Table:     spec.Table,
		Columns:   spec.Columns,
		Access:    spec.Access,
	}

	body, err := json.Marshal(payload)
	if err != nil {
		return DefineTableResult{}, fmt.Errorf("failed to marshal define-table body: %w", err)
	}

	req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/supabase/define-table", bytes.NewReader(body))
	if err != nil {
		return DefineTableResult{}, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	c.setHeaders(req)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return DefineTableResult{}, fmt.Errorf("request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode >= 500 {
		return DefineTableResult{}, fmt.Errorf("define-table endpoint returned HTTP %d", resp.StatusCode)
	}

	var result DefineTableResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return DefineTableResult{}, fmt.Errorf("failed to decode response: %w", err)
	}
	return result, nil
}
