from odoo.exceptions import AccessError

from .common import CommonTest


class TestFileRel(CommonTest):
    """Test class for git file relation."""

    def setUp(self):
        super().setUp()
        self.file_1_rel = self.GitProjectRel.create(
            {
                "server_id": self.server_test_1.id,
                "file_id": self.server_1_file_1.id,
                "git_project_id": self.git_project_1.id,
                "project_format": "git_aggregator",
            }
        )

    def test_file_rel_create(self):
        """Test if file relation is created correctly"""

        # -- 1 --
        # Check if file content is updated

        # Get code from project
        yaml_code_from_project = (
            self.file_1_rel.git_project_id._generate_code_git_aggregator(
                self.file_1_rel
            )
        )

        self.assertEqual(
            self.server_1_file_1.code,
            yaml_code_from_project,
            "File content is not updated correctly",
        )

        # Check specific if remote is present in file
        self.assertIn(
            self.remote_other_ssh.repo_id.url_ssh,
            self.server_1_file_1.code,
            "Remote is not present in file",
        )

        # -- 2 --
        # Modify remove and check if file content is updated
        self.remote_other_ssh.repo_id = self.Repo.create(
            {
                "url": "https://github.com/cetmix/cetmix-memes.git",
            }
        )
        self.remote_other_ssh.url_protocol = "https"

        # Must be different from previous project code
        self.assertNotEqual(
            self.server_1_file_1.code,
            yaml_code_from_project,
            "File content is not updated correctly",
        )
        # New remote must be present in file
        self.assertIn(
            "https://github.com/cetmix/cetmix-memes.git",
            self.server_1_file_1.code,
            "Remote is not present in file",
        )

        # -- 3 --
        # Disable source and check if file content is updated
        self.git_source_2.active = False
        self.assertNotIn(
            "https://github.com/cetmix/cetmix-memes.git",
            self.server_1_file_1.code,
            "Remote is present in file",
        )

    def test_format_git_aggregator(self):
        """Test if format git aggregator works correctly"""

        # -- 1 --
        # Check if YAML code is generated correctly

        yaml_code = """# This file is generated with Cetmix Tower https://cetmix.com/tower
# It's designed to be used with git-aggregator tool developed by Acsone.
# Documentation for git-aggregator: https://github.com/acsone/git-aggregator

# You need to set the following variables in your environment:
# BITBUCKET_TOKEN, GITLAB_TOKEN, GITLAB_TOKEN_NAME
# and run git-aggregator with '--expand-env' parameter.

./git_project_1_git_source_1:
  remotes:
    remote_1: https://github.com/cetmix-test/cetmix-tower-test.git
    remote_2: https://$GITLAB_TOKEN_NAME:$GITLAB_TOKEN@my.gitlab.com/cetmix-test/cetmix-tower-test.git
    remote_3: git@my.gitlab.com:cetmix-test/cetmix-tower-test.git
  merges:
  - remote: remote_1
    ref: refs/pull/123/head
  - remote: remote_2
    ref: main
  - remote: remote_3
    ref: '10000000'
  target: remote_1
./git_project_1_git_source_1_2:
  remotes:
    remote_1: https://x-token-auth:$BITBUCKET_TOKEN@bitbucket.com/cetmix-test/cetmix-tower-test-enterprise.git
    remote_2: git@memegit.com:cetmix-test/cetmix-tower-test.git
  merges:
  - remote: remote_1
    ref: dev
  - remote: remote_2
    ref: old
  target: remote_1
"""  # noqa: E501

        # Get code from project
        yaml_code_from_project = (
            self.file_1_rel.git_project_id._generate_code_git_aggregator(
                self.file_1_rel
            )
        )
        self.assertEqual(
            yaml_code_from_project,
            yaml_code,
            "YAML code is not generated correctly",
        )

        # -- 2 --
        # Unlink remote and check if file content is updated
        self.remote_github_https.unlink()
        yaml_code_from_project = (
            self.file_1_rel.git_project_id._generate_code_git_aggregator(
                self.file_1_rel
            )
        )
        yaml_code = """# This file is generated with Cetmix Tower https://cetmix.com/tower
# It's designed to be used with git-aggregator tool developed by Acsone.
# Documentation for git-aggregator: https://github.com/acsone/git-aggregator

# You need to set the following variables in your environment:
# BITBUCKET_TOKEN, GITLAB_TOKEN, GITLAB_TOKEN_NAME
# and run git-aggregator with '--expand-env' parameter.

./git_project_1_git_source_1:
  remotes:
    remote_2: https://$GITLAB_TOKEN_NAME:$GITLAB_TOKEN@my.gitlab.com/cetmix-test/cetmix-tower-test.git
    remote_3: git@my.gitlab.com:cetmix-test/cetmix-tower-test.git
  merges:
  - remote: remote_2
    ref: main
  - remote: remote_3
    ref: '10000000'
  target: remote_2
./git_project_1_git_source_1_2:
  remotes:
    remote_1: https://x-token-auth:$BITBUCKET_TOKEN@bitbucket.com/cetmix-test/cetmix-tower-test-enterprise.git
    remote_2: git@memegit.com:cetmix-test/cetmix-tower-test.git
  merges:
  - remote: remote_1
    ref: dev
  - remote: remote_2
    ref: old
  target: remote_1
"""  # noqa: E501

        self.assertEqual(
            yaml_code_from_project,
            yaml_code,
            "YAML code is not generated correctly",
        )

        # -- 3 --
        # Unlink source and check if file content is updated
        self.git_source_2.unlink()
        yaml_code_from_project = (
            self.file_1_rel.git_project_id._generate_code_git_aggregator(
                self.file_1_rel
            )
        )
        yaml_code = """# This file is generated with Cetmix Tower https://cetmix.com/tower
# It's designed to be used with git-aggregator tool developed by Acsone.
# Documentation for git-aggregator: https://github.com/acsone/git-aggregator

# You need to set the following variables in your environment:
# GITLAB_TOKEN, GITLAB_TOKEN_NAME
# and run git-aggregator with '--expand-env' parameter.

./git_project_1_git_source_1:
  remotes:
    remote_2: https://$GITLAB_TOKEN_NAME:$GITLAB_TOKEN@my.gitlab.com/cetmix-test/cetmix-tower-test.git
    remote_3: git@my.gitlab.com:cetmix-test/cetmix-tower-test.git
  merges:
  - remote: remote_2
    ref: main
  - remote: remote_3
    ref: '10000000'
  target: remote_2
"""  # noqa: E501
        self.assertEqual(
            yaml_code_from_project,
            yaml_code,
            "YAML code is not generated correctly",
        )

    def test_user_access(self):
        """Test that regular users have no access to git project relations"""
        user_rel = self.GitProjectRel.with_user(self.user)

        # Try create - should fail
        with self.assertRaises(AccessError):
            user_rel.create(
                {
                    "server_id": self.server_test_1.id,
                    "file_id": self.server_1_file_1.id,
                    "git_project_id": self.git_project_1.id,
                    "project_format": "git_aggregator",
                }
            )

        # Try read - should fail
        with self.assertRaises(AccessError):
            user_rel.browse(self.file_1_rel.id).read(["name"])

        # Try write - should fail
        with self.assertRaises(AccessError):
            user_rel.browse(self.file_1_rel.id).write(
                {"project_format": "git_aggregator"}
            )

        # Try unlink - should fail
        with self.assertRaises(AccessError):
            user_rel.browse(self.file_1_rel.id).unlink()

    def test_manager_read_access(self):
        """Test manager read access rules"""
        manager_rel = self.GitProjectRel.with_user(self.manager)

        # Initially manager should not have access
        with self.assertRaises(AccessError):
            manager_rel.browse(self.file_1_rel.id).read(["name"])

        # Add manager as project user - should have read access
        self.git_project_1.write({"user_ids": [(4, self.manager.id)]})
        self.assertEqual(manager_rel.browse(self.file_1_rel.id).name, "Git Project 1")

        # Remove from project, add as server user - should have read access
        self.git_project_1.write({"user_ids": [(3, self.manager.id)]})
        self.server_test_1.write({"user_ids": [(4, self.manager.id)]})
        self.assertEqual(manager_rel.browse(self.file_1_rel.id).name, "Git Project 1")

        # Remove from server users, add as project manager - should have read access
        self.server_test_1.write({"user_ids": [(3, self.manager.id)]})
        self.git_project_1.write({"manager_ids": [(4, self.manager.id)]})
        self.assertEqual(manager_rel.browse(self.file_1_rel.id).name, "Git Project 1")

        # Remove from project, add as server manager - should have read access
        self.git_project_1.write({"manager_ids": [(3, self.manager.id)]})
        self.server_test_1.write({"manager_ids": [(4, self.manager.id)]})
        self.assertEqual(manager_rel.browse(self.file_1_rel.id).name, "Git Project 1")

    def test_manager_write_access(self):
        """Test manager write/create access rules"""
        manager_rel = self.GitProjectRel.with_user(self.manager)

        # Create new file to avoid unique constraint violation
        file_2 = self.File.create(
            {
                "name": "test_file_2",
                "server_id": self.server_test_1.id,
                "source": "tower",
                "file_type": "text",
            }
        )

        # Try create without being project and server manager - should fail
        with self.assertRaises(AccessError):
            manager_rel.create(
                {
                    "server_id": self.server_test_1.id,
                    "file_id": file_2.id,
                    "git_project_id": self.git_project_1.id,
                    "project_format": "git_aggregator",
                }
            )

        # Add as project manager only - should still fail
        file_3 = self.File.create(
            {
                "name": "test_file_3",
                "server_id": self.server_test_1.id,
                "source": "tower",
                "file_type": "text",
            }
        )
        self.git_project_1.write({"manager_ids": [(4, self.manager.id)]})
        with self.assertRaises(AccessError):
            manager_rel.create(
                {
                    "server_id": self.server_test_1.id,
                    "file_id": file_3.id,
                    "git_project_id": self.git_project_1.id,
                    "project_format": "git_aggregator",
                }
            )

        # Add as server manager - should succeed
        file_4 = self.File.create(
            {
                "name": "test_file_4",
                "server_id": self.server_test_1.id,
                "source": "tower",
                "file_type": "text",
            }
        )
        self.server_test_1.write({"manager_ids": [(4, self.manager.id)]})
        rel = manager_rel.create(
            {
                "server_id": self.server_test_1.id,
                "file_id": file_4.id,
                "git_project_id": self.git_project_1.id,
                "project_format": "git_aggregator",
            }
        )
        self.assertTrue(rel.exists())

        # Test write access
        rel.write({"project_format": "git_aggregator"})

        # Remove server manager access - should fail to write
        self.server_test_1.write({"manager_ids": [(3, self.manager.id)]})
        with self.assertRaises(AccessError):
            rel.write({"project_format": "git_aggregator"})

        # Remove project manager access - should fail to write
        self.git_project_1.write({"manager_ids": [(3, self.manager.id)]})
        with self.assertRaises(AccessError):
            rel.write({"project_format": "git_aggregator"})

    def test_manager_unlink_access(self):
        """Test manager unlink access rules"""
        manager_rel = self.GitProjectRel.with_user(self.manager)

        # Try delete without being project and server manager - should fail
        with self.assertRaises(AccessError):
            manager_rel.browse(self.file_1_rel.id).unlink()

        # Add as project manager only - should fail
        self.git_project_1.write({"manager_ids": [(4, self.manager.id)]})
        with self.assertRaises(AccessError):
            manager_rel.browse(self.file_1_rel.id).unlink()

        # Add as server manager - should succeed
        self.server_test_1.write({"manager_ids": [(4, self.manager.id)]})
        self.file_1_rel.unlink()
        self.assertFalse(self.file_1_rel.exists())

    def test_root_access(self):
        """Test root access rules"""
        root_rel = self.GitProjectRel.with_user(self.root)

        # Create new file to avoid unique constraint violation
        file_3 = self.File.create(
            {
                "name": "test_file_3",
                "server_id": self.server_test_1.id,
                "source": "tower",
                "file_type": "text",
            }
        )

        # Create - should succeed
        rel = root_rel.create(
            {
                "server_id": self.server_test_1.id,
                "file_id": file_3.id,
                "git_project_id": self.git_project_1.id,
                "project_format": "git_aggregator",
            }
        )
        self.assertTrue(rel.exists())

        # Read - should succeed
        self.assertEqual(root_rel.browse(rel.id).name, "Git Project 1")

        # Write - should succeed
        root_rel.browse(rel.id).write({"project_format": "git_aggregator"})

        # Delete - should succeed
        rel.unlink()
        self.assertFalse(rel.exists())
