package gomodules

import (
	"fmt"
	"rubric/utils"
)

var StructuresModule = Module{
	Key:         "structs",
	Aliases:     []string{"s", "struct", "vars", "types"},
	Title:       "Структура программы",
	Description: "Имена, объявления, переменные, указатели, типы, пакеты, область видимости",
	Run:         showStructures,
}

func showStructures() {
	utils.Header("Go — Структура программы")

	// ─── Имена и ключевые слова ───────────────────────────────────
	utils.Section("Имена и ключевые слова")

	utils.Println("Имя начинается с буквы (Unicode) или символа подчёркивания, затем — буквы, цифры, _.")
	utils.Println("Регистр имеет значение: " +
		utils.ColorBold + utils.ColorCyan + "Name" + utils.ColorReset +
		" и " +
		utils.ColorBold + utils.ColorCyan + "name" + utils.ColorReset +
		" — разные идентификаторы.")
	fmt.Println()

	utils.Println(utils.ColorBold + utils.ColorYellow + "25 ключевых слов языка Go:" + utils.ColorReset)
	fmt.Println()
	keywords := [][]string{
		{"break", "case", "chan", "const", "continue"},
		{"default", "defer", "else", "fallthrough", "for"},
		{"func", "go", "goto", "if", "import"},
		{"interface", "map", "package", "range", "return"},
		{"select", "struct", "switch", "type", "var"},
	}
	for _, row := range keywords {
		fmt.Print("    ")
		for _, kw := range row {
			fmt.Printf("%s%-14s%s", utils.ColorCyan+utils.ColorBold, kw, utils.ColorReset)
		}
		fmt.Println()
	}
	fmt.Println()

	utils.Println(utils.ColorBold + utils.ColorYellow + "Предопределённые имена (не ключевые слова — можно переопределить, но не стоит):" + utils.ColorReset)
	fmt.Println()
	utils.Println(utils.ColorDim + "  Типы:" + utils.ColorReset)
	utils.Println("    bool  byte  rune  string  error")
	utils.Println("    int  int8  int16  int32  int64")
	utils.Println("    uint  uint8  uint16  uint32  uint64  uintptr")
	utils.Println("    float32  float64  complex64  complex128")
	fmt.Println()
	utils.Println(utils.ColorDim + "  Константы:" + utils.ColorReset)
	utils.Println("    true  false  iota")
	fmt.Println()
	utils.Println(utils.ColorDim + "  Нулевое значение:" + utils.ColorReset)
	utils.Println("    nil")
	fmt.Println()
	utils.Println(utils.ColorDim + "  Встроенные функции:" + utils.ColorReset)
	utils.Println("    append  cap  close  complex  copy  delete  imag")
	utils.Println("    len  make  new  panic  print  println  real  recover")
	fmt.Println()
	utils.Note("Если имя начинается с заглавной буквы — оно экспортируется из пакета (публичное).")
	utils.Note("Стиль: camelCase для локальных, CamelCase для экспортируемых. Аббревиатуры — заглавными: URL, ID, HTTP.")

	// ─── Объявления ───────────────────────────────────────────────
	utils.Section("Объявления")

	utils.Println("В Go четыре вида объявлений верхнего уровня:")
	fmt.Println()
	utils.Code(`package main

import "fmt"

// const — константа верхнего уровня
const Greeting = "Привет"

// type — новый тип
type Celsius float64

// var — переменная верхнего уровня
var boilingPoint Celsius = 100

// func — функция
func main() {
    fmt.Println(Greeting, boilingPoint)
}`)
	utils.Println("Порядок объявлений на верхнем уровне не важен — компилятор разрешает зависимости сам.")

	// ─── Переменные ───────────────────────────────────────────────
	utils.Section("Переменные и способы объявления")

	utils.Println(utils.ColorBold + "1. Полное объявление через var:" + utils.ColorReset)
	utils.Code(`var имя тип = выражение

var x int           // x == 0   (нулевое значение)
var s string        // s == ""
var b bool          // b == false
var p *int          // p == nil

// Несколько переменных одного типа:
var i, j, k int

// Несколько переменных разных типов:
var (
    name    string  = "Gopher"
    age     int     = 3
    active  bool    = true
)`)

	utils.Println(utils.ColorBold + "2. Краткое объявление := (только внутри функций):" + utils.ColorReset)
	utils.Code(`имя := выражение          // тип выводится автоматически

x := 42               // int
s := "hello"          // string
f := 3.14             // float64
b := true             // bool

// Несколько переменных:
i, j := 0, 1
name, err := os.Open("file.txt")  // часто используется для ошибок

// ВАЖНО: хотя бы одна переменная слева должна быть новой:
x, y := 1, 2   // ok: обе новые
x, y = 3, 4    // ok: присваивание (уже объявлены)
x, z := 5, 6   // ok: z — новая, x — переприсваивается`)

	utils.Println(utils.ColorBold + "3. Нулевые значения (zero values):" + utils.ColorReset)
	utils.Code(`// Каждый тип имеет нулевое значение при объявлении без инициализации:
var i    int        // 0
var f    float64    // 0.0
var b    bool       // false
var s    string     // ""
var p    *int       // nil
var sl   []int      // nil
var m    map[string]int   // nil
var fn   func()     // nil
var err  error      // nil`)
	utils.Note("В Go нет неинициализированных переменных — нулевое значение гарантировано.")

	utils.Println(utils.ColorBold + "4. Объявление нескольких переменных с var-блоком:" + utils.ColorReset)
	utils.Code(`// Группировка для читаемости:
var (
    debug   = false
    version = "1.0.0"
    maxConn = 100
)`)

	// ─── Указатели ────────────────────────────────────────────────
	utils.Section("Указатели")

	utils.Println("Указатель хранит " + utils.ColorBold + "адрес" + utils.ColorReset + " переменной в памяти.")
	utils.Code(`var x int = 42

p := &x        // p — указатель на x, тип *int
fmt.Println(p)  // 0xc0000b4008  (адрес)
fmt.Println(*p) // 42            (разыменование — значение по адресу)

*p = 100        // изменить значение x через указатель
fmt.Println(x)  // 100

// Указатель на указатель:
pp := &p        // тип **int
fmt.Println(**pp) // 100`)

	utils.Code(`// Нулевое значение указателя — nil:
var p *int
fmt.Println(p == nil) // true
// *p = 1  — паника! нельзя разыменовывать nil-указатель

// Сравнение: указатели равны, если указывают на один адрес:
a, b := 1, 1
pa, pb := &a, &b
fmt.Println(pa == pb)  // false (разные переменные)
pa2 := &a
fmt.Println(pa == pa2) // true (один и тот же адрес)`)

	utils.Note("Go — безопасный язык: нет арифметики указателей (без пакета unsafe).")
	utils.Note("Указатель позволяет функции изменять переменную вызывающего кода.")

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

	utils.Println("new(T) выделяет память для значения типа T, инициализирует нулевым значением,")
	utils.Println("возвращает указатель *T.")
	utils.Code(`p := new(int)         // *int, *p == 0
fmt.Println(*p)       // 0
*p = 7
fmt.Println(*p)       // 7

// Эквивалентно:
var tmp int
p2 := &tmp

// new для структур:
type Point struct{ X, Y int }
pt := new(Point)      // *Point, pt.X==0, pt.Y==0
pt.X = 10`)

	utils.Note("new используется редко — чаще пишут &T{...} для структур или var x T; return &x.")
	utils.Code(`// Эти три записи эквивалентны:
p1 := new(int)

var v int
p2 := &v

p3 := &struct{ int }{} // анонимная структура — другой случай`)

	// ─── Время жизни переменных ───────────────────────────────────
	utils.Section("Время жизни переменных")

	utils.Println("Время жизни — период, в течение которого переменная существует в памяти.")
	fmt.Println()
	utils.Println(utils.ColorBold + "Переменные пакетного уровня:" + utils.ColorReset +
		" живут всё время работы программы.")
	utils.Println(utils.ColorBold + "Локальные переменные:" + utils.ColorReset +
		" создаются при каждом выполнении объявления, удаляются когда становятся недостижимы.")
	fmt.Println()
	utils.Code(`// Escape analysis — компилятор решает, где хранить переменную:
// стек (быстро) или куча (heap, медленнее, но живёт дольше).

func newInt() *int {
    v := 42          // v "убегает" на кучу (heap escape),
    return &v        // потому что возвращается её адрес.
}

func sum(a, b int) int {
    result := a + b  // result на стеке — не "убегает",
    return result    // возвращается значение, не адрес.
}`)
	utils.Note("Не нужно думать о стеке/куче вручную — GC Go управляет памятью автоматически.")
	utils.Note("go build -gcflags='-m' — показывает, какие переменные уходят на кучу.")

	// ─── Присваивания ─────────────────────────────────────────────
	utils.Section("Присваивания")

	utils.Println(utils.ColorBold + "Простое присваивание:" + utils.ColorReset)
	utils.Code(`x = 1
*p = true
person.name = "Алиса"
count[x] = count[x] * scale`)

	utils.Println(utils.ColorBold + "Составные операторы присваивания:" + utils.ColorReset)
	utils.Code(`x += 1    x -= 1    x *= 2    x /= 2    x %= 3
x &= y    x |= y    x ^= y    x <<= 2   x >>= 1`)

	utils.Println(utils.ColorBold + "Инкремент и декремент (только постфиксные, только как операторы):" + utils.ColorReset)
	utils.Code(`x++   // x = x + 1
x--   // x = x - 1

// ВАЖНО: в Go нет префиксных ++/-- и нет выражений с ++/--:
// ++x     — ошибка компиляции
// y = x++ — ошибка компиляции`)

	utils.Println(utils.ColorBold + "Множественное присваивание (tuple assignment):" + utils.ColorReset)
	utils.Code(`// Все правые части вычисляются ДО записи в левые:
x, y = y, x              // обмен значениями

a, b, c := 1, 2, 3
a, b, c = b, c, a        // сдвиг

// Возврат нескольких значений из функции:
quotient, remainder := divide(17, 5)

// Игнорирование значения через _:
_, err = fmt.Println("hi")`)

	utils.Println(utils.ColorBold + "Присваиваемость (assignability):" + utils.ColorReset)
	utils.Code(`// Присваивание допустимо, если:
// 1. Типы идентичны
// 2. Типы имеют одинаковую базу и хотя бы один — безымянный
// 3. T реализует интерфейс I
// 4. Присваивание nil совместимому типу (chan, func, interface, map, pointer, slice)

var i interface{} = "строка"   // string → interface{}
var n int = 42
var f float64 = float64(n)     // явное преобразование типов`)

	// ─── Объявления типов ────────────────────────────────────────
	utils.Section("Объявления типов")

	utils.Println("type создаёт " + utils.ColorBold + "новый именованный тип" + utils.ColorReset + " с тем же базовым представлением.")
	utils.Code(`type Celsius    float64
type Fahrenheit float64

var boiling Celsius = 100

// Celsius и Fahrenheit — РАЗНЫЕ типы, хотя оба float64:
// boiling = Fahrenheit(212)   — ошибка компиляции!

// Явное преобразование разрешено (одно базовое представление):
f := Fahrenheit(boiling*9/5 + 32)
fmt.Println(f) // 212`)

	utils.Code(`// Методы можно добавлять только к именованным типам:
func (c Celsius) String() string {
    return fmt.Sprintf("%.2f°C", float64(c))
}

fmt.Println(boiling) // 100.00°C  (автоматически вызывается String())`)

	utils.Code(`// type-алиас (псевдоним) — НЕ создаёт новый тип, это то же самое:
type Rune = int32    // rune — именно так определён в builtin

// Отличие: алиас и оригинал взаимозаменяемы без конверсии.
// Обычный type — нет.`)

	utils.Note("type позволяет добавлять методы к базовым типам (int, string, slice и т.д.).")
	utils.Note("Явное преобразование T(v) работает если оба типа имеют одно представление.")

	// ─── Пакеты, файлы и импорт ──────────────────────────────────
	utils.Section("Пакеты, файлы и импорт")

	utils.Println("Пакет — единица инкапсуляции и переиспользования в Go.")
	utils.Println("Все .go-файлы в одной директории — один пакет. Имя объявляется в первой строке.")
	fmt.Println()
	utils.Code(`// Каждый файл начинается с объявления пакета:
package main        // исполняемая программа
package utils       // библиотечный пакет
package http        // пакет стандартной библиотеки`)

	utils.Println(utils.ColorBold + "Импорт пакетов:" + utils.ColorReset)
	utils.Code(`// Одиночный импорт:
import "fmt"

// Групповой импорт (предпочтительный стиль):
import (
    "fmt"
    "os"
    "strings"

    "github.com/user/project/utils"   // внешний пакет
)`)

	utils.Println(utils.ColorBold + "Формы импорта:" + utils.ColorReset)
	utils.Code(`import "fmt"                // стандартный: fmt.Println(...)
import f "fmt"             // псевдоним: f.Println(...)
import . "fmt"             // слияние: Println(...)  (не рекомендуется)
import _ "image/png"       // только для side-эффектов (init)`)

	utils.Note("Неиспользуемый импорт — ошибка компиляции. Используйте _ для подавления.")
	utils.Note("Имя пакета и имя директории могут отличаться, но по соглашению совпадают.")
	utils.Code(`// Путь импорта vs имя пакета:
import "math/rand"     // путь: math/rand
rand.Intn(10)          // имя пакета: rand

import "crypto/rand"   // другой пакет, то же имя!
// Нужен алиас:
import (
    mrand "math/rand"
    crand "crypto/rand"
)`)

	// ─── Инициализация пакетов ────────────────────────────────────
	utils.Section("Инициализация пакетов")

	utils.Println("Инициализация переменных пакетного уровня происходит в порядке объявления,")
	utils.Println("с учётом зависимостей между ними.")
	utils.Code(`// Порядок гарантирован: a, b, c
var a = 1
var b = a + 1    // 2
var c = b * 2    // 4

// Сложные случаи — через функцию init():
var lookup = buildLookup()

func buildLookup() map[string]int {
    // ...
    return m
}`)

	utils.Println(utils.ColorBold + "Функция init():" + utils.ColorReset)
	utils.Code(`// init — специальная функция инициализации пакета:
// - вызывается автоматически при запуске программы
// - нельзя вызвать явно
// - в одном файле может быть несколько init()
// - в одном пакете — несколько init() в разных файлах

func init() {
    // подготовка состояния, регистрация, настройка
    log.SetFlags(log.Lshortfile)
}`)

	utils.Println(utils.ColorBold + "Порядок инициализации всей программы:" + utils.ColorReset)
	utils.Code(`// 1. Инициализируются импортируемые пакеты (рекурсивно, от листьев к корню)
// 2. Переменные пакетного уровня в порядке объявления / зависимостей
// 3. Вызываются все функции init() данного пакета
// 4. ... переход к следующему пакету
// 5. Последним инициализируется пакет main
// 6. Вызывается main.main()

// import _ "pkg"  — импортировать только ради init()
// Пример: регистрация драйвера БД:
import _ "github.com/lib/pq"   // PostgreSQL driver`)

	utils.Note("Порядок вызова init() между файлами одного пакета определяется алфавитным порядком имён файлов.")

	// ─── Область видимости ───────────────────────────────────────
	utils.Section("Область видимости (Scope)")

	utils.Println("Область видимости — часть кода, в которой объявление действует.")
	utils.Println("Это " + utils.ColorBold + "статическое" + utils.ColorReset + " свойство (на уровне компиляции), в отличие от времени жизни (runtime).")
	fmt.Println()
	utils.Code(`// Уровни области видимости (от широкого к узкому):
// 1. Вселенная (universe) — встроенные имена: int, len, nil...
// 2. Пакет     — объявления на верхнем уровне файла
// 3. Файл      — только import-пути
// 4. Локальный — блок { }, параметры функции, for, if, switch...

var x = "пакет"      // область видимости — весь пакет

func f() {
    fmt.Println(x)   // "пакет"
    x := "локальный" // новая переменная x (shadowing!)
    fmt.Println(x)   // "локальный"
    {
        x := "вложенный"
        fmt.Println(x) // "вложенный"
    }
    fmt.Println(x)   // "локальный"
}`)

	utils.Println(utils.ColorBold + "Затенение (shadowing) в управляющих конструкциях:" + utils.ColorReset)
	utils.Code(`// if-инициализатор:
if x := compute(); x > 0 {
    // x видна только здесь...
} else {
    // ...и здесь
}
// x здесь уже не видна

// Распространённая ошибка с := в if:
var err error
if err := doA(); err != nil {  // новая err!
    return err
}
// err (внешняя) всё ещё nil — doA не изменил её!
fmt.Println(err) // nil`)

	utils.Println(utils.ColorBold + "Экспортируемые имена — публичный API пакета:" + utils.ColorReset)
	utils.Code(`package geometry

// Экспортируется (заглавная буква):
type Point struct {
    X, Y float64   // поля тоже экспортируются
}

func Distance(p, q Point) float64 { ... }

// НЕ экспортируется (строчная буква):
type circle struct { center Point; radius float64 }
func area(c circle) float64 { ... }`)

	utils.Note("Экспортируемость — единственный механизм инкапсуляции в Go. Нет private/protected/public ключевых слов.")
	utils.Note("Правило: экспортируй только то, что является частью публичного API. Всё остальное — строчными буквами.")

	utils.Footer()
}
