package server

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"net/http"

	"odoo-go/pkg/orm"
)

// handleExportCSV exports records as CSV.
// Mirrors: odoo/addons/web/controllers/export.py ExportController
func (s *Server) handleExportCSV(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var req JSONRPCRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.writeJSONRPC(w, nil, nil, &RPCError{Code: -32700, Message: "Parse error"})
		return
	}

	var params struct {
		Data struct {
			Model  string          `json:"model"`
			Fields []exportField   `json:"fields"`
			Domain []interface{}   `json:"domain"`
			IDs    []float64       `json:"ids"`
		} `json:"data"`
	}
	if err := json.Unmarshal(req.Params, &params); err != nil {
		s.writeJSONRPC(w, req.ID, nil, &RPCError{Code: -32602, Message: "Invalid params"})
		return
	}

	// Extract UID from session
	uid := int64(1)
	companyID := int64(1)
	if sess := GetSession(r); sess != nil {
		uid = sess.UID
		companyID = sess.CompanyID
	}

	env, err := orm.NewEnvironment(r.Context(), orm.EnvConfig{
		Pool:      s.pool,
		UID:       uid,
		CompanyID: companyID,
	})
	if err != nil {
		http.Error(w, "Internal error", http.StatusInternalServerError)
		return
	}
	defer env.Close()

	rs := env.Model(params.Data.Model)

	// Determine which record IDs to export
	var ids []int64
	if len(params.Data.IDs) > 0 {
		for _, id := range params.Data.IDs {
			ids = append(ids, int64(id))
		}
	} else {
		// Search with domain
		domain := parseDomain([]interface{}{params.Data.Domain})
		found, err := rs.Search(domain, orm.SearchOpts{Limit: 10000})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		ids = found.IDs()
	}

	if len(ids) == 0 {
		w.Header().Set("Content-Type", "text/csv")
		w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.csv", params.Data.Model))
		return
	}

	// Extract field names
	var fieldNames []string
	var headers []string
	for _, f := range params.Data.Fields {
		fieldNames = append(fieldNames, f.Name)
		label := f.Label
		if label == "" {
			label = f.Name
		}
		headers = append(headers, label)
	}

	// Read records
	records, err := rs.Browse(ids...).Read(fieldNames)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if err := env.Commit(); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Write CSV
	w.Header().Set("Content-Type", "text/csv; charset=utf-8")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.csv", params.Data.Model))

	writer := csv.NewWriter(w)
	defer writer.Flush()

	// Header row
	writer.Write(headers)

	// Data rows
	for _, rec := range records {
		row := make([]string, len(fieldNames))
		for i, fname := range fieldNames {
			row[i] = formatCSVValue(rec[fname])
		}
		writer.Write(row)
	}
}

// exportField describes a field in an export request.
type exportField struct {
	Name  string `json:"name"`
	Label string `json:"label"`
}

// formatCSVValue converts a field value to a CSV string.
func formatCSVValue(v interface{}) string {
	if v == nil || v == false {
		return ""
	}
	switch val := v.(type) {
	case string:
		return val
	case bool:
		if val {
			return "True"
		}
		return "False"
	case []interface{}:
		// M2O: [id, "name"] → "name"
		if len(val) == 2 {
			if name, ok := val[1].(string); ok {
				return name
			}
		}
		return fmt.Sprintf("%v", val)
	default:
		return fmt.Sprintf("%v", val)
	}
}
