package gomodules

import (
	"fmt"
	"rubric/utils"
)

var CompositeModule = Module{
	Key:         "composite",
	Aliases:     []string{"c", "comp", "slice", "map", "struct"},
	Title:       "Составные типы",
	Description: "Массивы, срезы, append, отображения, структуры, JSON и HTML",
	Run:         showComposite,
}

func showComposite() {
	utils.Header("Go — Составные типы")

	// ─── Массивы ──────────────────────────────────────────────────
	utils.Section("Массивы (Arrays)")

	utils.Println("Массив — фиксированной длины последовательность элементов одного типа.")
	utils.Println("Длина является частью типа: " +
		utils.ColorBold + utils.ColorCyan + "[3]int" + utils.ColorReset +
		" и " +
		utils.ColorBold + utils.ColorCyan + "[4]int" + utils.ColorReset +
		" — разные типы.")
	fmt.Println()
	utils.Code(`// Объявление:
var a [3]int                      // [0 0 0] — нулевые значения
var b [3]int = [3]int{1, 2, 3}
c := [3]int{1, 2, 3}
d := [...]int{1, 2, 3, 4, 5}     // компилятор считает длину сам

// Инициализация с индексами:
e := [5]string{1: "один", 3: "три"}  // остальные — ""

// Доступ:
fmt.Println(c[0])      // 1
fmt.Println(len(c))    // 3

// Итерация:
for i, v := range c {
    fmt.Printf("c[%d] = %d\n", i, v)
}`)

	utils.Println(utils.ColorBold + "Сравнение массивов:" + utils.ColorReset)
	utils.Code(`// Массивы можно сравнивать через == и !=
// (если элементы сравниваемы):
a := [3]int{1, 2, 3}
b := [3]int{1, 2, 3}
fmt.Println(a == b)    // true

// Передача в функцию — КОПИЯ:
func double(arr [3]int) { arr[0] *= 2 }  // оригинал не меняется
// Чтобы изменить — передавать указатель: *[3]int`)

	utils.Note("Массивы в Go используются редко — чаще работают со срезами.")

	// ─── Срезы ────────────────────────────────────────────────────
	utils.Section("Срезы (Slices)")

	utils.Println("Срез — динамический вид на массив. Три компонента:")
	utils.Code(`// Указатель на массив + длина (len) + ёмкость (cap)
//
// ┌─────────────────────────────────────────┐
// │  ptr  │  len  │  cap                    │
// └───┬───┴───────┴─────────────────────────┘
//     │
//     ▼
// ┌───┬───┬───┬───┬───┐
// │ 0 │ 1 │ 2 │ 3 │ 4 │  ← underlying array
// └───┴───┴───┴───┴───┘`)

	utils.Println(utils.ColorBold + "Создание срезов:" + utils.ColorReset)
	utils.Code(`// Из литерала (неявно создаёт массив):
s := []int{1, 2, 3, 4, 5}

// Срез нулевых значений через make(тип, len, cap):
s := make([]int, 5)        // len=5, cap=5
s := make([]int, 3, 10)    // len=3, cap=10

// Нулевой срез (nil slice):
var s []int                // s == nil, len=0, cap=0
fmt.Println(s == nil)      // true
// nil срез безопасен для append и range

// Срез из массива:
a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]                // элементы 1,2,3 → [2 3 4]
                           // len=3, cap=4 (до конца массива)`)

	utils.Println(utils.ColorBold + "Операции срезов:" + utils.ColorReset)
	utils.Code(`s := []int{1, 2, 3, 4, 5}

// Взятие среза: s[low:high]  (low включён, high — нет)
s[1:3]    // [2 3]       len=2
s[:3]     // [1 2 3]     low=0
s[2:]     // [3 4 5]     high=len
s[:]      // копия ссылки на весь срез

// Трёхиндексный срез: s[low:high:max] — ограничивает cap:
s[1:3:4]  // len=2, cap=3

// len и cap:
fmt.Println(len(s), cap(s))   // 5 5

// Срезы разделяют память с оригиналом!
a := []int{1, 2, 3, 4, 5}
b := a[1:3]                   // b = [2 3]
b[0] = 99
fmt.Println(a)                // [1 99 3 4 5] — оригинал изменён!`)

	utils.Println(utils.ColorBold + "Копирование срезов (copy):" + utils.ColorReset)
	utils.Code(`src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3)
n := copy(dst, src)           // копирует min(len(dst), len(src)) элементов
fmt.Println(dst, n)           // [1 2 3] 3

// Полная независимая копия:
clone := make([]int, len(src))
copy(clone, src)

// Или через append:
clone := append([]int{}, src...)`)

	utils.Println(utils.ColorBold + "Итерация:" + utils.ColorReset)
	utils.Code(`s := []string{"a", "b", "c"}

for i, v := range s {          // v — копия элемента
    fmt.Println(i, v)
}

for i := range s {             // только индексы
    s[i] = strings.ToUpper(s[i])
}

for _, v := range s {          // только значения
    fmt.Println(v)
}`)

	// ─── append ───────────────────────────────────────────────────
	utils.Section("Функция append")

	utils.Println("append добавляет элементы в срез, возвращая новый (возможно, с новым массивом).")
	utils.Code(`// Добавить элементы:
s := []int{1, 2, 3}
s = append(s, 4)              // [1 2 3 4]
s = append(s, 5, 6, 7)       // [1 2 3 4 5 6 7]

// Добавить другой срез (оператор ...):
other := []int{8, 9}
s = append(s, other...)       // [1 2 3 4 5 6 7 8 9]

// Удалить элемент по индексу i:
i := 2
s = append(s[:i], s[i+1:]...) // порядок сохраняется
// Быстрое удаление (порядок не важен):
s[i] = s[len(s)-1]
s = s[:len(s)-1]

// Вставить элемент по индексу i:
s = append(s[:i+1], s[i:]...)
s[i] = newValue

// Стек (stack):
push := func(s []int, v int) []int { return append(s, v) }
pop  := func(s []int) ([]int, int) { return s[:len(s)-1], s[len(s)-1] }`)

	utils.Println(utils.ColorBold + "Механизм роста ёмкости:" + utils.ColorReset)
	utils.Code(`// При нехватке cap, append выделяет новый массив (~удваивает cap):
var s []int
for i := 0; i < 8; i++ {
    s = append(s, i)
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
// len=1 cap=1  [0]
// len=2 cap=2  [0 1]
// len=3 cap=4  [0 1 2]
// len=4 cap=4  [0 1 2 3]
// len=5 cap=8  [0 1 2 3 4]
// ...`)

	utils.Note("Всегда присваивай результат append обратно: s = append(s, v)")
	utils.Note("Передавай срез с запасом через make([]T, 0, n) если знаешь итоговый размер — избежишь лишних аллокаций.")

	// ─── Отображения ──────────────────────────────────────────────
	utils.Section("Отображения (Maps)")

	utils.Println("Map — ссылочный тип, хэш-таблица. Порядок итерации не гарантирован.")
	fmt.Println()
	utils.Println(utils.ColorBold + "Создание:" + utils.ColorReset)
	utils.Code(`// Через литерал:
ages := map[string]int{
    "Alice": 30,
    "Bob":   25,
}

// Через make:
ages := make(map[string]int)
ages := make(map[string]int, 100)  // с подсказкой ёмкости

// Нулевое значение — nil:
var m map[string]int    // m == nil
// m["key"] = 1         // паника! нельзя писать в nil map
// fmt.Println(m["key"]) // 0 — читать можно, не паникует`)

	utils.Println(utils.ColorBold + "Операции с map:" + utils.ColorReset)
	utils.Code(`ages := map[string]int{"Alice": 30, "Bob": 25}

// Запись:
ages["Carol"] = 28
ages["Alice"] = 31      // обновить

// Чтение:
age := ages["Alice"]    // 31
age  = ages["unknown"]  // 0 — нулевое значение, НЕ ошибка

// Проверка наличия ключа (comma ok idiom):
age, ok := ages["Alice"]
if ok {
    fmt.Println("Alice:", age)
}
// Коротко:
if age, ok := ages["Alice"]; ok {
    fmt.Println("Alice:", age)
}

// Удаление:
delete(ages, "Bob")
delete(ages, "nonexistent")  // безопасно, нет ошибки

// Длина:
fmt.Println(len(ages))   // 2

// Итерация (порядок случаен!):
for name, age := range ages {
    fmt.Printf("%s: %d\n", name, age)
}

// Итерация по отсортированным ключам:
keys := make([]string, 0, len(ages))
for k := range ages { keys = append(keys, k) }
sort.Strings(keys)
for _, k := range keys { fmt.Println(k, ages[k]) }`)

	utils.Println(utils.ColorBold + "Сравнение map:" + utils.ColorReset)
	utils.Code(`// Map нельзя сравнивать через == (только с nil):
var m map[string]int
fmt.Println(m == nil)   // true

// Сравнение содержимого — через reflect.DeepEqual или вручную:
import "reflect"
fmt.Println(reflect.DeepEqual(map1, map2))

// Или maps.Equal из Go 1.21:
import "maps"
fmt.Println(maps.Equal(map1, map2))`)

	utils.Println(utils.ColorBold + "Типичные паттерны:" + utils.ColorReset)
	utils.Code(`// Множество (set):
seen := make(map[string]bool)
seen["Alice"] = true
if seen["Alice"] { ... }

// Подсчёт слов:
words := strings.Fields(text)
count := make(map[string]int)
for _, w := range words { count[w]++ }

// Группировка:
byDept := make(map[string][]Employee)
for _, e := range employees {
    byDept[e.Dept] = append(byDept[e.Dept], e)
}`)

	// ─── Структуры ────────────────────────────────────────────────
	utils.Section("Структуры (Structs)")

	utils.Println("Struct — агрегатный тип, объединяет поля разных типов.")
	utils.Code(`// Объявление типа:
type Point struct {
    X, Y float64
}

type Employee struct {
    ID        int
    Name      string
    Salary    float64
    Manager   *Employee    // рекурсивный тип — только указатель
}`)

	utils.Println(utils.ColorBold + "Создание и доступ:" + utils.ColorReset)
	utils.Code(`// Структурный литерал (по именам — рекомендуется):
p := Point{X: 1.0, Y: 2.0}
p := Point{1.0, 2.0}       // по позиции (хрупко, не для публичных типов)

// Нулевое значение:
var p Point                 // Point{X:0, Y:0}

// Доступ к полям:
fmt.Println(p.X)            // 1.0
p.Y = 5.0

// Указатель на структуру:
pp := &Point{1.0, 2.0}
pp.X = 10                  // (*pp).X = 10 — автодеференс
fmt.Println(pp.X)          // 10

// Через new:
pp := new(Point)            // *Point, поля = нули
pp.X = 1.0`)

	utils.Println(utils.ColorBold + "Сравнение структур:" + utils.ColorReset)
	utils.Code(`// Структуры сравниваемы если все поля сравниваемы:
type Point struct{ X, Y int }
p1 := Point{1, 2}
p2 := Point{1, 2}
fmt.Println(p1 == p2)   // true

// Поля-срезы, map, func — делают структуру несравниваемой.
// Используй reflect.DeepEqual для глубокого сравнения.

// Структура как ключ map (только если сравниваема):
type Coord struct{ Lat, Lon float64 }
visited := map[Coord]bool{}
visited[Coord{55.75, 37.62}] = true`)

	// ─── Встраивание и анонимные поля ────────────────────────────
	utils.Section("Встраивание структур и анонимные поля")

	utils.Println("Встраивание (embedding) — включение типа без имени поля.")
	utils.Println("Поля и методы встроенного типа «поднимаются» во внешний тип.")
	utils.Code(`type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return a.Name + " говорит"
}

type Dog struct {
    Animal          // анонимное поле — встраивание
    Breed string
}

d := Dog{
    Animal: Animal{Name: "Шарик"},
    Breed:  "Лабрадор",
}

// Продвинутый доступ (promotion):
fmt.Println(d.Name)      // "Шарик"   (d.Animal.Name)
fmt.Println(d.Speak())   // "Шарик говорит" (d.Animal.Speak())
fmt.Println(d.Breed)     // "Лабрадор"
// Явный доступ:
fmt.Println(d.Animal.Name)`)

	utils.Code(`// Несколько встраиваний:
type Logger struct{ Prefix string }
func (l Logger) Log(msg string) { fmt.Println(l.Prefix, msg) }

type Server struct {
    Logger                   // анонимное поле
    net.Listener             // встроен интерфейс
    Host string
}

s := Server{Logger: Logger{Prefix: "[server]"}, Host: "localhost"}
s.Log("started")             // s.Logger.Log("started")

// Конфликт имён — нужно явно указывать путь:
type A struct{ X int }
type B struct{ X int }
type C struct { A; B }
c := C{}
// c.X              — ошибка компиляции: ambiguous
c.A.X = 1          // явно`)

	utils.Code(`// Встраивание указателя:
type Cache struct {
    *sync.Mutex        // анонимный указатель
    data map[string]string
}

c := Cache{
    Mutex: &sync.Mutex{},
    data:  make(map[string]string),
}
c.Lock()                    // c.Mutex.Lock()
c.data["key"] = "value"
c.Unlock()`)

	utils.Note("Встраивание — это композиция, не наследование. Go не имеет наследования.")

	// ─── Работа с JSON ────────────────────────────────────────────
	utils.Section("Работа с JSON")

	utils.Println("Пакет " + utils.ColorBold + utils.ColorCyan + "encoding/json" + utils.ColorReset +
		" — маршалинг и демаршалинг JSON.")
	fmt.Println()
	utils.Println(utils.ColorBold + "Теги структур:" + utils.ColorReset)
	utils.Code(`type Person struct {
    Name    string  ` + "`" + `json:"name"` + "`" + `
    Age     int     ` + "`" + `json:"age"` + "`" + `
    Email   string  ` + "`" + `json:"email,omitempty"` + "`" + `  // пропустить если пусто
    Phone   string  ` + "`" + `json:"-"` + "`" + `               // никогда не включать
    private string                            // неэкспортируемые — игнорируются
}

// Теги:
// json:"name"          — имя в JSON
// json:"name,omitempty"— пропустить нулевые значения
// json:"-"             — исключить поле`)

	utils.Println(utils.ColorBold + "Маршалинг (структура → JSON):" + utils.ColorReset)
	utils.Code(`import "encoding/json"

p := Person{Name: "Alice", Age: 30, Email: "alice@example.com"}

// Сериализация:
data, err := json.Marshal(p)
if err != nil { log.Fatal(err) }
fmt.Println(string(data))
// {"name":"Alice","age":30,"email":"alice@example.com"}

// Красивый вывод с отступами:
data, err = json.MarshalIndent(p, "", "  ")
fmt.Println(string(data))
// {
//   "name": "Alice",
//   "age": 30,
//   "email": "alice@example.com"
// }`)

	utils.Println(utils.ColorBold + "Демаршалинг (JSON → структура):" + utils.ColorReset)
	utils.Code(`jsonStr := ` + "`" + `{"name":"Bob","age":25}` + "`" + `

var p Person
if err := json.Unmarshal([]byte(jsonStr), &p); err != nil {
    log.Fatal(err)
}
fmt.Println(p.Name, p.Age)   // Bob 25

// Потоковое чтение из http.Response:
resp, _ := http.Get("https://api.example.com/users")
defer resp.Body.Close()
var users []Person
json.NewDecoder(resp.Body).Decode(&users)

// Потоковая запись в http.ResponseWriter:
json.NewEncoder(w).Encode(users)`)

	utils.Println(utils.ColorBold + "Произвольный JSON (interface{} / any):" + utils.ColorReset)
	utils.Code(`// Когда структура неизвестна:
var result map[string]any
json.Unmarshal(data, &result)

name := result["name"].(string)          // type assertion
age  := result["age"].(float64)          // числа → float64 по умолчанию

// Проверка типа:
if s, ok := result["name"].(string); ok {
    fmt.Println(s)
}

// json.RawMessage — отложенный демаршалинг:
type Response struct {
    Status  string          ` + "`" + `json:"status"` + "`" + `
    Payload json.RawMessage ` + "`" + `json:"payload"` + "`" + `
}
var resp Response
json.Unmarshal(data, &resp)
var user Person
json.Unmarshal(resp.Payload, &user)`)

	utils.Println(utils.ColorBold + "Кастомные маршалеры:" + utils.ColorReset)
	utils.Code(`// Реализовать json.Marshaler / json.Unmarshaler:
type Duration time.Duration

func (d Duration) MarshalJSON() ([]byte, error) {
    return json.Marshal(time.Duration(d).String())
}

func (d *Duration) UnmarshalJSON(b []byte) error {
    var s string
    if err := json.Unmarshal(b, &s); err != nil { return err }
    dur, err := time.ParseDuration(s)
    *d = Duration(dur)
    return err
}`)

	// ─── Работа с HTML ────────────────────────────────────────────
	utils.Section("Работа с HTML")

	utils.Println("Пакет " + utils.ColorBold + utils.ColorCyan + "html/template" + utils.ColorReset +
		" — безопасные HTML-шаблоны с автоматическим экранированием.")
	fmt.Println()
	utils.Code(`import "html/template"

// Шаблон:
const tmpl = ` + "`" + `
<!DOCTYPE html>
<html>
<body>
    <h1>Привет, {{.Name}}!</h1>
    <ul>
    {{range .Items}}
        <li>{{.}}</li>
    {{end}}
    </ul>
    {{if .Admin}}
        <p>Вы администратор</p>
    {{end}}
</body>
</html>` + "`" + `

type Data struct {
    Name  string
    Items []string
    Admin bool
}

// Парсинг и выполнение:
t := template.Must(template.New("page").Parse(tmpl))

data := Data{
    Name:  "Alice",
    Items: []string{"Go", "Docker", "Kubernetes"},
    Admin: true,
}

// В os.Stdout:
t.Execute(os.Stdout, data)

// В http.ResponseWriter:
func handler(w http.ResponseWriter, r *http.Request) {
    t.Execute(w, data)
}`)

	utils.Println(utils.ColorBold + "Экранирование и безопасность:" + utils.ColorReset)
	utils.Code(`// html/template автоматически экранирует пользовательский ввод:
// <script>alert(1)</script>  →  &lt;script&gt;alert(1)&lt;/script&gt;

// Если нужно вставить безопасный HTML — использовать template.HTML:
type Page struct {
    Title   string
    Content template.HTML  // не экранируется
}

// Никогда не делай:
// template.HTML(userInput)  — XSS!

// text/template — без экранирования (для не-HTML):
import "text/template"
// Используй для генерации кода, конфигов, emails в plain text`)

	utils.Println(utils.ColorBold + "Шаблонные функции и Actions:" + utils.ColorReset)
	utils.Code(`// Функции в шаблоне:
funcMap := template.FuncMap{
    "upper": strings.ToUpper,
    "add":   func(a, b int) int { return a + b },
}
t := template.New("t").Funcs(funcMap).Parse(` + "`" + `
{{.Name | upper}}
{{add 2 3}}
` + "`" + `)

// Actions (действия):
{{/* комментарий */}}
{{.Field}}                    // вывести поле
{{.Method arg}}               // вызов метода
{{if .Cond}} ... {{end}}
{{if .Cond}} ... {{else}} ... {{end}}
{{range .Slice}} {{.}} {{end}}
{{range .Map}} {{.}} {{end}}
{{with .Val}} {{.}} {{end}}   // установить . = Val если не ноль
{{template "name" .}}         // вложенный шаблон

// Шаблоны из файлов:
t := template.Must(template.ParseFiles("index.html", "base.html"))
t := template.Must(template.ParseGlob("templates/*.html"))`)

	utils.Println(utils.ColorBold + "Парсинг HTML (html пакет):" + utils.ColorReset)
	utils.Code(`import "html"

// Экранирование и разэкранирование HTML-сущностей:
escaped := html.EscapeString("<script>alert('xss')</script>")
// &lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;

unescaped := html.UnescapeString("&lt;b&gt;жирный&lt;/b&gt;")
// <b>жирный</b>

// Для разбора HTML-дерева — golang.org/x/net/html:
import "golang.org/x/net/html"

resp, _ := http.Get("https://example.com")
doc, _ := html.Parse(resp.Body)

// Рекурсивный обход:
var walk func(*html.Node)
walk = func(n *html.Node) {
    if n.Type == html.ElementNode && n.Data == "a" {
        for _, attr := range n.Attr {
            if attr.Key == "href" {
                fmt.Println(attr.Val)
            }
        }
    }
    for c := n.FirstChild; c != nil; c = c.NextSibling {
        walk(c)
    }
}
walk(doc)`)

	utils.Footer()
}
