# Copyright (C) 2021 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from pathlib import Path
import tempfile

import pytest

import ansys.fluent.core as pyfluent
from ansys.fluent.core.examples import download_file
from ansys.fluent.core.solver import *  # noqa: F401, F403
from ansys.fluent.core.solver.flobject import InactiveObjectError
from ansys.fluent.core.utils.fluent_version import FluentVersion

# flake8: noqa: F405


@pytest.mark.codegen_required
def test_builtin_settings(mixing_elbow_case_data_session):
    solver = mixing_elbow_case_data_session
    fluent_version = solver.get_fluent_version()
    assert Setup(settings_source=solver) == solver.setup
    assert General(settings_source=solver) == solver.setup.general
    assert Models(settings_source=solver) == solver.setup.models
    assert Multiphase(settings_source=solver) == solver.setup.models.multiphase
    assert Energy(settings_source=solver) == solver.setup.models.energy
    assert Viscous(settings_source=solver) == solver.setup.models.viscous
    assert Radiation(settings_source=solver) == solver.setup.models.radiation
    assert Species(settings_source=solver) == solver.setup.models.species
    assert DiscretePhase(settings_source=solver) == solver.setup.models.discrete_phase
    assert (
        Injections(settings_source=solver)
        == solver.setup.models.discrete_phase.injections
    )
    assert (
        VirtualBladeModel(settings_source=solver)
        == solver.setup.models.virtual_blade_model
    )
    assert Optics(settings_source=solver) == solver.setup.models.optics
    assert Structure(settings_source=solver) == solver.setup.models.structure
    assert Ablation(settings_source=solver) == solver.setup.models.ablation
    assert EChemistry(settings_source=solver) == solver.setup.models.echemistry
    assert Battery(settings_source=solver) == solver.setup.models.battery
    assert SystemCoupling(settings_source=solver) == solver.setup.models.system_coupling
    assert Sofc(settings_source=solver) == solver.setup.models.sofc
    assert Pemfc(settings_source=solver) == solver.setup.models.pemfc
    assert Materials(settings_source=solver) == solver.setup.materials
    assert FluidMaterials(settings_source=solver) == solver.setup.materials.fluid
    assert (
        FluidMaterial(settings_source=solver, name="air")
        == solver.setup.materials.fluid["air"]
    )
    assert SolidMaterials(settings_source=solver) == solver.setup.materials.solid
    assert (
        SolidMaterial(settings_source=solver, name="aluminum")
        == solver.setup.materials.solid["aluminum"]
    )
    assert (
        CellZoneConditions(settings_source=solver) == solver.setup.cell_zone_conditions
    )
    assert (
        CellZoneCondition(settings_source=solver, name="elbow-fluid")
        == solver.setup.cell_zone_conditions["elbow-fluid"]
    )
    assert (
        FluidCellZones(settings_source=solver)
        == solver.setup.cell_zone_conditions.fluid
    )
    assert (
        FluidCellZone(settings_source=solver, name="elbow-fluid")
        == solver.setup.cell_zone_conditions.fluid["elbow-fluid"]
    )
    assert (
        BoundaryConditions(settings_source=solver) == solver.setup.boundary_conditions
    )
    assert (
        BoundaryCondition(settings_source=solver, name="cold-inlet")
        == solver.setup.boundary_conditions["cold-inlet"]
    )
    with pytest.raises(TypeError):
        BoundaryCondition(settings_source=solver, new_instance_name="bc-1")
    assert (
        VelocityInlets(settings_source=solver)
        == solver.setup.boundary_conditions.velocity_inlet
    )
    assert (
        VelocityInlet(settings_source=solver, name="cold-inlet")
        == solver.setup.boundary_conditions.velocity_inlet["cold-inlet"]
    )
    assert (
        InteriorBoundaries(settings_source=solver)
        == solver.setup.boundary_conditions.interior
    )
    assert (
        PressureOutlets(settings_source=solver)
        == solver.setup.boundary_conditions.pressure_outlet
    )
    assert (
        PressureOutlet(settings_source=solver, name="outlet")
        == solver.setup.boundary_conditions.pressure_outlet["outlet"]
    )
    assert (
        WallBoundaries(settings_source=solver) == solver.setup.boundary_conditions.wall
    )
    assert (
        WallBoundary(settings_source=solver, name="wall-elbow")
        == solver.setup.boundary_conditions.wall["wall-elbow"]
    )
    if fluent_version < FluentVersion.v251:
        with pytest.raises(AttributeError):
            WallBoundary(settings_source=solver, new_instance_name="wall-1")
    assert MeshInterfaces(settings_source=solver) == solver.setup.mesh_interfaces
    if fluent_version >= FluentVersion.v251:
        assert DynamicMesh(settings_source=solver) == solver.setup.dynamic_mesh
    else:
        with pytest.raises(RuntimeError):
            DynamicMesh(settings_source=solver)
    assert ReferenceValues(settings_source=solver) == solver.setup.reference_values
    assert ReferenceFrames(settings_source=solver) == solver.setup.reference_frames
    # Fluent 25.1 issue
    if fluent_version != FluentVersion.v251:
        assert (
            ReferenceFrame(settings_source=solver, name="global")
            == solver.setup.reference_frames["global"]
        )
    assert NamedExpressions(settings_source=solver) == solver.setup.named_expressions
    assert Methods(settings_source=solver) == solver.solution.methods
    assert Controls(settings_source=solver) == solver.solution.controls
    assert (
        ReportDefinitions(settings_source=solver) == solver.solution.report_definitions
    )
    assert Monitor(settings_source=solver) == solver.solution.monitor
    assert Residual(settings_source=solver) == solver.solution.monitor.residual
    assert ReportFiles(settings_source=solver) == solver.solution.monitor.report_files
    assert (
        ReportFile(settings_source=solver, new_instance_name="report-file-1")
        == solver.solution.monitor.report_files["report-file-1"]
    )
    assert (
        ReportFile(settings_source=solver, name="report-file-1")
        == solver.solution.monitor.report_files["report-file-1"]
    )
    if fluent_version >= FluentVersion.v251:
        assert (
            ReportFile(settings_source=solver)
            == solver.solution.monitor.report_files["report-file-2"]
        )
    assert ReportPlots(settings_source=solver) == solver.solution.monitor.report_plots
    assert (
        ReportPlot(settings_source=solver, new_instance_name="report-plot-1")
        == solver.solution.monitor.report_plots["report-plot-1"]
    )
    assert (
        ReportPlot(settings_source=solver, name="report-plot-1")
        == solver.solution.monitor.report_plots["report-plot-1"]
    )
    if fluent_version >= FluentVersion.v251:
        assert (
            ReportPlot(settings_source=solver)
            == solver.solution.monitor.report_plots["report-plot-2"]
        )
    assert (
        ConvergenceConditions(settings_source=solver)
        == solver.solution.monitor.convergence_conditions
    )
    assert CellRegisters(settings_source=solver) == solver.solution.cell_registers
    assert (
        CellRegister(settings_source=solver, new_instance_name="cell_register_1")
        == solver.solution.cell_registers["cell_register_1"]
    )
    assert (
        CellRegister(settings_source=solver, name="cell_register_1")
        == solver.solution.cell_registers["cell_register_1"]
    )
    if fluent_version >= FluentVersion.v251:
        assert (
            CellRegister(settings_source=solver)
            == solver.solution.cell_registers["cell_register_2"]
        )
    assert Initialization(settings_source=solver) == solver.solution.initialization
    assert (
        CalculationActivity(settings_source=solver)
        == solver.solution.calculation_activity
    )
    assert (
        ExecuteCommands(settings_source=solver)
        == solver.solution.calculation_activity.execute_commands
    )
    assert (
        CaseModification(settings_source=solver)
        == solver.solution.calculation_activity.case_modification
    )
    assert RunCalculation(settings_source=solver) == solver.solution.run_calculation
    assert Solution(settings_source=solver) == solver.solution
    assert Results(settings_source=solver) == solver.results
    assert Surfaces(settings_source=solver) == solver.results.surfaces
    assert (
        PointSurfaces(settings_source=solver) == solver.results.surfaces.point_surface
    )
    assert LineSurfaces(settings_source=solver) == solver.results.surfaces.line_surface
    assert RakeSurfaces(settings_source=solver) == solver.results.surfaces.rake_surface
    assert IsoSurfaces(settings_source=solver) == solver.results.surfaces.iso_surface
    assert (
        PlaneSurfaces(settings_source=solver) == solver.results.surfaces.plane_surface
    )
    assert (
        PlaneSurface(settings_source=solver, new_instance_name="plane-1")
        == solver.results.surfaces.plane_surface["plane-1"]
    )
    assert (
        PlaneSurface(settings_source=solver, name="plane-1")
        == solver.results.surfaces.plane_surface["plane-1"]
    )
    assert IsoClips(settings_source=solver) == solver.results.surfaces.iso_clip
    assert ZoneSurfaces(settings_source=solver) == solver.results.surfaces.zone_surface
    assert (
        PartitionSurfaces(settings_source=solver)
        == solver.results.surfaces.partition_surface
    )
    assert (
        TransformSurfaces(settings_source=solver)
        == solver.results.surfaces.transform_surface
    )
    assert (
        ImprintSurfaces(settings_source=solver)
        == solver.results.surfaces.imprint_surface
    )
    assert PlaneSlices(settings_source=solver) == solver.results.surfaces.plane_slice
    assert SphereSlices(settings_source=solver) == solver.results.surfaces.sphere_slice
    assert (
        QuadricSurfaces(settings_source=solver)
        == solver.results.surfaces.quadric_surface
    )
    assert SurfaceCells(settings_source=solver) == solver.results.surfaces.surface_cells
    if fluent_version >= FluentVersion.v251:
        assert (
            ExpressionVolumes(settings_source=solver)
            == solver.results.surfaces.expression_volume
        )
        assert (
            GroupSurfaces(settings_source=solver)
            == solver.results.surfaces.group_surface
        )
    else:
        with pytest.raises(RuntimeError):
            ExpressionVolumes(settings_source=solver)
        with pytest.raises(RuntimeError):
            GroupSurfaces(settings_source=solver)
    assert Graphics(settings_source=solver) == solver.results.graphics
    assert Meshes(settings_source=solver) == solver.results.graphics.mesh
    assert Contours(settings_source=solver) == solver.results.graphics.contour
    assert (
        Contour(settings_source=solver, new_instance_name="contour-1")
        == solver.results.graphics.contour["contour-1"]
    )
    assert (
        Contour(settings_source=solver, name="contour-1")
        == solver.results.graphics.contour["contour-1"]
    )
    assert Vectors(settings_source=solver) == solver.results.graphics.vector
    assert LICs(settings_source=solver) == solver.results.graphics.lic
    assert Pathlines(settings_source=solver) == solver.results.graphics.pathline
    assert (
        ParticleTracks(settings_source=solver) == solver.results.graphics.particle_track
    )
    assert Plots(settings_source=solver) == solver.results.plot
    assert XYPlots(settings_source=solver) == solver.results.plot.xy_plot
    assert Histogram(settings_source=solver) == solver.results.plot.histogram
    assert (
        CumulativePlots(settings_source=solver) == solver.results.plot.cumulative_plot
    )
    assert ProfileData(settings_source=solver) == solver.results.plot.profile_data
    assert (
        InterpolatedData(settings_source=solver)
        == solver.results.plot.interpolated_data
    )
    assert Scenes(settings_source=solver) == solver.results.scene
    assert (
        SceneAnimation(settings_source=solver)
        == solver.results.animations.scene_animation
    )
    assert Report(settings_source=solver) == solver.results.report
    assert (
        DiscretePhaseHistogram(settings_source=solver)
        == solver.results.report.discrete_phase.histogram
    )
    assert Fluxes(settings_source=solver) == solver.results.report.fluxes
    assert (
        SurfaceIntegrals(settings_source=solver)
        == solver.results.report.surface_integrals
    )
    assert (
        VolumeIntegrals(settings_source=solver)
        == solver.results.report.volume_integrals
    )
    assert (
        SimulationReports(settings_source=solver)
        == solver.results.report.simulation_reports
    )
    assert InputParameters(settings_source=solver) == solver.parameters.input_parameters
    assert (
        OutputParameters(settings_source=solver) == solver.parameters.output_parameters
    )
    if fluent_version >= FluentVersion.v251:
        assert (
            CustomFieldFunctions(settings_source=solver)
            == solver.results.custom_field_functions
        )
    else:
        with pytest.raises(RuntimeError):
            CustomFieldFunctions(settings_source=solver)
    assert CustomVectors(settings_source=solver) == solver.results.custom_vectors
    solver.settings.parametric_studies.initialize(
        project_filename="mixing_elbow_param.flprj"
    )
    assert ParametricStudies(settings_source=solver) == solver.parametric_studies
    assert (
        ParametricStudy(settings_source=solver, name="mixing_elbow-Solve")
        == solver.parametric_studies["mixing_elbow-Solve"]
    )
    assert (
        DesignPoints(settings_source=solver, parametric_studies="mixing_elbow-Solve")
        == solver.parametric_studies["mixing_elbow-Solve"].design_points
    )
    assert (
        DesignPoint(
            settings_source=solver,
            parametric_studies="mixing_elbow-Solve",
            name="Base DP",
        )
        == solver.parametric_studies["mixing_elbow-Solve"].design_points["Base DP"]
    )
    assert ReadCase(settings_source=solver) == solver.file.read_case
    assert ReadData(settings_source=solver) == solver.file.read_data
    assert ReadCaseData(settings_source=solver) == solver.file.read_case_data
    assert WriteCase(settings_source=solver) == solver.file.write_case
    assert WriteData(settings_source=solver) == solver.file.write_data
    assert WriteCaseData(settings_source=solver) == solver.file.write_case_data
    assert (
        Initialize(settings_source=solver) == solver.solution.initialization.initialize
    )
    assert (
        Calculate(settings_source=solver) == solver.solution.run_calculation.calculate
    )
    assert Iterate(settings_source=solver) == solver.solution.run_calculation.iterate
    assert (
        DualTimeIterate(settings_source=solver)
        == solver.solution.run_calculation.dual_time_iterate
    )


@pytest.mark.codegen_required
@pytest.mark.fluent_version(">=23.2")
def test_builtin_singleton_setting_assign_session(
    new_meshing_session, new_solver_session
):
    meshing = new_meshing_session
    solver = new_solver_session

    models = Models()
    assert isinstance(models, Models)
    with pytest.raises(TypeError):
        models.settings_source = meshing
    models.settings_source = solver
    assert models.settings_source == solver.settings
    assert not isinstance(models, Models)
    assert models.path == "setup/models"
    assert not models.is_active()
    case_name = download_file("Static_Mixer_main.cas.h5", "pyfluent/static_mixer")
    solver.file.read(
        file_type="case",
        file_name=case_name,
        lightweight_setup=True,
    )
    assert models.is_active()
    assert models == solver.setup.models
    with pytest.raises(AttributeError):  # re-assignment is not allowed
        models.settings_source = solver

    models = Models()
    assert isinstance(models, Models)
    models.settings_source = solver.settings
    assert models == solver.setup.models
    assert models.settings_source == solver.settings


@pytest.mark.codegen_required
@pytest.mark.fluent_version(">=23.2")
def test_builtin_non_creatable_named_object_setting_assign_session(
    new_meshing_session, static_mixer_case_session
):
    meshing = new_meshing_session
    solver = static_mixer_case_session

    inlet = BoundaryCondition(name="inlet1")
    assert isinstance(inlet, BoundaryCondition)
    with pytest.raises(TypeError):
        inlet.settings_source = meshing
    inlet.settings_source = solver
    assert inlet == solver.settings.setup.boundary_conditions["inlet1"]
    assert inlet.settings_source == solver.settings
    with pytest.raises(AttributeError):  # re-assignment is not allowed
        inlet.settings_source = solver

    inlet = BoundaryCondition(name="inlet1")
    assert isinstance(inlet, BoundaryCondition)
    inlet.settings_source = solver.settings
    assert inlet == solver.settings.setup.boundary_conditions["inlet1"]
    assert inlet.settings_source == solver.settings


@pytest.mark.codegen_required
@pytest.mark.fluent_version(">=23.2")
def test_builtin_creatable_named_object_setting_assign_session(
    new_meshing_session, static_mixer_case_session
):
    meshing = new_meshing_session
    solver = static_mixer_case_session

    report_file = ReportFile(new_instance_name="report-file-1")
    assert isinstance(report_file, ReportFile)
    with pytest.raises(TypeError):
        report_file.settings_source = meshing
    report_file.settings_source = solver
    assert report_file == solver.solution.monitor.report_files["report-file-1"]
    assert report_file.settings_source == solver.settings
    with pytest.raises(AttributeError):  # re-assignment is not allowed
        report_file.settings_source = solver

    report_file = ReportFile(name="report-file-1")
    assert isinstance(report_file, ReportFile)
    report_file.settings_source = solver
    assert report_file == solver.solution.monitor.report_files["report-file-1"]
    assert report_file.settings_source == solver.settings

    report_file = ReportFile(name="report-file-1")
    assert isinstance(report_file, ReportFile)
    report_file.settings_source = solver.settings
    assert report_file == solver.solution.monitor.report_files["report-file-1"]
    assert report_file.settings_source == solver.settings

    if solver.get_fluent_version() >= FluentVersion.v251:
        report_file = ReportFile()
        assert isinstance(report_file, ReportFile)
        report_file.settings_source = solver
        assert report_file == solver.solution.monitor.report_files["report-file-2"]
        assert report_file.settings_source == solver.settings


@pytest.mark.codegen_required
def test_context_manager_1(mixing_elbow_case_data_session):
    import threading

    solver = mixing_elbow_case_data_session

    # Test that the context manager works with a solver session
    with using(solver):
        assert Setup() == solver.setup
        assert General() == solver.setup.general
        assert Models() == solver.setup.models
        assert Multiphase() == solver.setup.models.multiphase
        assert Energy() == solver.setup.models.energy
        assert Viscous() == solver.setup.models.viscous

    # Test that the context manager works with multiple threads
    def run_solver_context():
        with using(solver):
            setup = Setup()
            print(
                f"Setup in thread {threading.current_thread().name}: {setup == solver.setup}"
            )

    threads = [
        threading.Thread(target=run_solver_context, name=f"Thread-{i}")
        for i in range(3)
    ]
    for t in threads:
        t.start()
    for t in threads:
        t.join()


@pytest.mark.codegen_required
def test_context_manager_2(new_solver_session):
    solver = new_solver_session

    import_filename = download_file(
        "elbow.cas.h5",
        "pyfluent/examples/DOE-ML-Mixing-Elbow",
    )

    with using(solver):
        read_case(file_name=import_filename)
        assert Viscous().model() == "k-omega"


@pytest.mark.codegen_required
def test_inactive_objects(new_solver_session):
    solver = new_solver_session

    with pytest.raises(InactiveObjectError):
        pyfluent.solver.Viscous(solver)
