package itemprops

import (
	"encoding/json"
	"os"
	"testing"
	"time"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/shopspring/decimal"
	"github.com/stretchr/testify/suite"
)

type IpTestSuite struct {
    suite.Suite
}

// метод для установки тестовых данных для всего набора, запускается до всего прочего
func (suite *IpTestSuite) SetupSuite() {
}

// метод для сноса наделанного тестом до идеально чистого состояния, запускается после всего прочего
func (suite *IpTestSuite) TearDownSuite() {
}

// запускается перед каждым тестом
func (suite *IpTestSuite) SetupTest() {}

// запускается после каждого теста
func (suite *IpTestSuite) TearDownTest() {}

func TestApi(t *testing.T) {
    tests := new(IpTestSuite)
    suite.Run(t, tests)
}

func (suite *IpTestSuite) TestMaterialWeightAndVolume() {
    // плотность https://tekkos.ru/katalog/poleznaya-informatsiya/tablica-plotnosti-stali-kg-m3.html
    // ударная вязкость https://nposanef.ru/DOCUMENTS/PB-03-605-03/PB-03-605-03_Tab-2.6.pdf
    // температура плавления http://zaozmi.ru/polezno/temperatura_plavleniya_metallov.html
    // температура кипения http://temperatures.ru/pages/temperatura_plavleniya_i_kipeniya
    // Пределы прочности некоторых материалов https://sevparitet.ru/raznoe/koefficient-uprugosti-tablica.html

    metalMaterialFlags := MaterialFlags{
        ConductsElictricity: true,
        BlocksLiquid:        true,
        AcidResistant:       true,
        BlocksGas:           true,
        Flammable:           false,
        ConductsHeat:        true,
        Radiates:            true,
    }

    woodMaterialFlags := MaterialFlags{
        ConductsElictricity: false,
        BlocksLiquid:        true,
        AcidResistant:       false,
        BlocksGas:           true,
        Flammable:           true,
        ConductsHeat:        false,
        Radiates:            false,
    }

    teststeel := Material{
        Id:                "teststeel",
        Name:              "steel",
        Flags:             metalMaterialFlags,
        Density:           DimensionItemDensity{decimal.NewFromInt(7800)},
        FractureToughness: DimensionFractureToughness{decimal.NewFromInt(30)},
        MeltingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(1400), Valid: true}},
        BoilingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(3200), Valid: true}},
    }

    testOakWood := Material{
        Id:                "testoakwood",
        Name:              "oakwood",
        Flags:             woodMaterialFlags,
        Density:           DimensionItemDensity{decimal.NewFromInt(700)},
        FractureToughness: DimensionFractureToughness{decimal.NewFromFloat(4.5)},
        MeltingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(600), Valid: true}}, //загорается при 600 град Цельсия
    }

    testCube := ItemPhysics{
        Material:              teststeel,
        DimensionItemRigidity: DimensionItemRigidity{decimal.NewFromInt(65)},
        DimensionItemSize: DimensionItemSize{
            Width:     decimal.NewFromFloat(0.1),
            Height:    decimal.NewFromFloat(0.1),
            Depth:     decimal.NewNullDecimal(decimal.NewFromFloat(0.1)),
            Thickness: decimal.NullDecimal{},
        },
        DimensionItemTemperature: DimensionItemTemperature{decimal.NewFromInt(20)},
    }

    suite.Equal(decimal.NewFromFloat(7.8).String(), testCube.Weight().String())
    suite.Equal(decimal.NewFromFloat(0.001).String(), testCube.Volume().String())

    testOakCube := ItemPhysics{
        Material:              testOakWood,
        DimensionItemRigidity: DimensionItemRigidity{decimal.NewFromInt(4)},
        DimensionItemSize: DimensionItemSize{
            Width:     decimal.NewFromFloat(0.1),
            Height:    decimal.NewFromFloat(0.1),
            Depth:     decimal.NewNullDecimal(decimal.NewFromFloat(0.1)),
            Thickness: decimal.NullDecimal{},
        },
        DimensionItemTemperature: DimensionItemTemperature{decimal.NewFromInt(20)},
    }

    //oakwood is ~10 times lighter than steel
    suite.Equal(decimal.NewFromFloat(0.7).String(), testOakCube.Weight().String())
    suite.Equal(decimal.NewFromFloat(0.001).String(), testOakCube.Volume().String())

    testCuirass := ItemPhysics{
        Material:              teststeel,
        DimensionItemRigidity: DimensionItemRigidity{decimal.NewFromInt(55)},
        DimensionItemSize: DimensionItemSize{
            Width:     decimal.NewFromFloat(0.5),                           //60 cm wide
            Height:    decimal.NewFromFloat(0.8),                           //80 cm high
            Depth:     decimal.NewNullDecimal(decimal.NewFromFloat(0.4)),   //50 cm deep
            Thickness: decimal.NewNullDecimal(decimal.NewFromFloat(0.001)), // 1mm thick
        },
        DimensionItemTemperature: DimensionItemTemperature{},
    }

    //12.1992 kg HEAVY ARMOR IS HEAVY
    suite.Equal(decimal.NewFromFloat(12.1992).String(), testCuirass.Weight().String())
    //0.001564 m3 of steel
    suite.Equal(decimal.NewFromFloat(0.001564).String(), testCuirass.Volume().String())
}

func (suite *IpTestSuite) TestMaterialSerialization() {
    metalMaterialFlags := MaterialFlags{
        ConductsElictricity: true,
        BlocksLiquid:        true,
        AcidResistant:       true,
        BlocksGas:           true,
        Flammable:           false,
        ConductsHeat:        true,
        Radiates:            true,
    }

    woodMaterialFlags := MaterialFlags{
        ConductsElictricity: false,
        BlocksLiquid:        true,
        AcidResistant:       false,
        BlocksGas:           true,
        Flammable:           true,
        ConductsHeat:        false,
        Radiates:            false,
    }

    teststeel := Material{
        Id:                "teststeel",
        Name:              "steel",
        Flags:             metalMaterialFlags,
        Density:           DimensionItemDensity{decimal.NewFromInt(7800)},
        FractureToughness: DimensionFractureToughness{decimal.NewFromInt(30)},
        MeltingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(1400), Valid: true}},
        BoilingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(3200), Valid: true}},
    }

    testOakWood := Material{
        Id:                "testoakwood",
        Name:              "oakwood",
        Flags:             woodMaterialFlags,
        Density:           DimensionItemDensity{decimal.NewFromInt(700)},
        FractureToughness: DimensionFractureToughness{decimal.NewFromFloat(4.5)},
        MeltingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(600), Valid: true}}, //загорается при 600 град Цельсия
    }
    bytes, err := json.Marshal(teststeel)
    suite.NoError(err)
    suite.Equal(
        `{"id":"teststeel","name":"steel","material_flags":{"conducts_elictricity":true,"blocks_liquid":true,"acid_resistant":true,"blocks_gas":true,"flammable":false,"conducts_heat":true,"radiates":true},"density":"7800","fracture_toughness":"30","melting_point":"1400","boiling_point":"3200"}`,
        string(bytes),
    )

    bytes, err = json.Marshal(testOakWood)
    suite.NoError(err)
    suite.Equal(`{"id":"testoakwood","name":"oakwood","material_flags":{"conducts_elictricity":false,"blocks_liquid":true,"acid_resistant":false,"blocks_gas":true,"flammable":true,"conducts_heat":false,"radiates":false},"density":"700","fracture_toughness":"4.5","melting_point":"600","boiling_point":null}`,
        string(bytes),
    )
}

func (suite *IpTestSuite) TestMaterialDeserialization() {

    logger := log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339})
    mm, err := NewMaterialMap("../../..", logger)
    suite.NoError(err)

    metalMaterialFlags := MaterialFlags{
        ConductsElictricity: true,
        BlocksLiquid:        true,
        AcidResistant:       true,
        BlocksGas:           true,
        Flammable:           false,
        ConductsHeat:        true,
        Radiates:            true,
    }

    teststeel := Material{
        Id:                "steel",
        Name:              "steel",
        Flags:             metalMaterialFlags,
        Density:           DimensionItemDensity{decimal.NewFromInt(7800)},
        FractureToughness: DimensionFractureToughness{decimal.NewFromInt(30)},
        MeltingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(1400), Valid: true}},
        BoilingPoint:      DimensionItemNullTemperature{decimal.NullDecimal{Decimal: decimal.NewFromInt(3200), Valid: true}},
    }

    loadedsteel := mm["steel"]
    suite.Equalf(teststeel, *loadedsteel, "error: %s")
    _ = mm
}
