from django.contrib.auth.models import User
from django.test import Client, TestCase
from django.urls import reverse

from financial.models import (
    AddressModel,
    ChargeNumber,
    Contract,
    Employee,
    TimeCard,
    TimeCardCell,
    UserProfile,
    set_user_type,
)
from financial.permissions import get_employees, is_client_user, is_employee_user


class UserProfileSignalTests(TestCase):
    def test_new_user_gets_client_profile_not_employee(self):
        user = User.objects.create_user(username="newbie", password="pass")
        self.assertTrue(UserProfile.objects.filter(user=user, user_type=UserProfile.UserType.CLIENT).exists())
        self.assertFalse(Employee.objects.filter(user=user).exists())


class SetUserTypeTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username="worker", password="pass")
        UserProfile.objects.filter(user=self.user).delete()

    def test_set_employee_creates_employee_record(self):
        set_user_type(self.user, UserProfile.UserType.EMPLOYEE)
        self.assertTrue(Employee.objects.filter(user=self.user).exists())
        self.assertEqual(self.user.profile.user_type, UserProfile.UserType.EMPLOYEE)

    def test_set_client_removes_employee_without_time_entries(self):
        set_user_type(self.user, UserProfile.UserType.EMPLOYEE)
        set_user_type(self.user, UserProfile.UserType.CLIENT)
        self.assertFalse(Employee.objects.filter(user=self.user).exists())
        self.assertEqual(
            UserProfile.objects.get(user=self.user).user_type,
            UserProfile.UserType.CLIENT,
        )

    def test_cannot_set_client_with_time_entries(self):
        set_user_type(self.user, UserProfile.UserType.EMPLOYEE)
        employee = Employee.objects.get(user=self.user)
        contract = Contract.objects.create(
            contract_type=Contract.ContractTypeEnum.FIRM_FIX_PRICED,
            name="Test Contract",
        )
        charge = ChargeNumber.objects.create(
            charge_number_type=ChargeNumber.ChargeNumberTypeEnum.LEVEL_OF_EFFORT,
            contract=contract,
        )
        time_card = TimeCard.objects.create(employee=employee)
        TimeCardCell.objects.create(timeCard=time_card, charge_number=charge, hour=2.0)

        with self.assertRaises(ValueError):
            set_user_type(self.user, UserProfile.UserType.CLIENT)


class EmployeeFilterTests(TestCase):
    def setUp(self):
        self.employee_user = User.objects.create_user(username="emp", password="pass")
        self.client_user = User.objects.create_user(username="cli", password="pass")
        set_user_type(self.employee_user, UserProfile.UserType.EMPLOYEE)
        set_user_type(self.client_user, UserProfile.UserType.CLIENT)

    def test_get_employees_excludes_clients(self):
        employees = list(get_employees())
        self.assertEqual(len(employees), 1)
        self.assertEqual(employees[0].user, self.employee_user)

    def test_permission_helpers(self):
        self.assertTrue(is_employee_user(self.employee_user))
        self.assertFalse(is_employee_user(self.client_user))
        self.assertTrue(is_client_user(self.client_user))
        self.assertFalse(is_client_user(self.employee_user))


class FinancialAccessTests(TestCase):
    def setUp(self):
        self.client = Client()
        self.admin = User.objects.create_superuser(username="admin", password="pass", email="a@test.com")
        self.employee = User.objects.create_user(username="employee", password="pass")
        self.client_user = User.objects.create_user(username="client", password="pass")
        set_user_type(self.employee, UserProfile.UserType.EMPLOYEE)
        set_user_type(self.client_user, UserProfile.UserType.CLIENT)

        contract = Contract.objects.create(
            contract_type=Contract.ContractTypeEnum.FIRM_FIX_PRICED,
            name="C1",
            budget_hours=100,
        )
        charge = ChargeNumber.objects.create(
            charge_number_type=ChargeNumber.ChargeNumberTypeEnum.LEVEL_OF_EFFORT,
            contract=contract,
        )
        emp_record = Employee.objects.get(user=self.employee)
        time_card = TimeCard.objects.create(employee=emp_record)
        TimeCardCell.objects.create(timeCard=time_card, charge_number=charge, hour=4.0)

    def test_client_can_view_reports_readonly(self):
        self.client.login(username="client", password="pass")
        response = self.client.get(reverse("client_reports"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "C1")

    def test_client_cannot_log_time(self):
        self.client.login(username="client", password="pass")
        response = self.client.get(reverse("Timekeeping"))
        self.assertEqual(response.status_code, 403)

    def test_client_can_view_time_logs_without_edit(self):
        self.client.login(username="client", password="pass")
        response = self.client.get(reverse("time_logs"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Read only")
        self.assertNotContains(response, 'href="/financial/time_logs/')

    def test_employee_can_access_timekeeping(self):
        self.client.login(username="employee", password="pass")
        response = self.client.get(reverse("Timekeeping"))
        self.assertEqual(response.status_code, 200)

    def test_client_redirected_from_financial_home_to_reports(self):
        self.client.login(username="client", password="pass")
        response = self.client.get(reverse("financial_home"))
        self.assertRedirects(response, reverse("client_reports"))

    def test_admin_can_manage_users(self):
        self.client.login(username="admin", password="pass")
        response = self.client.get(reverse("manage_users"))
        self.assertEqual(response.status_code, 200)
        response = self.client.post(reverse("manage_users"), {
            "user": self.client_user.id,
            "user_type": UserProfile.UserType.EMPLOYEE,
        })
        self.assertRedirects(response, reverse("manage_users"))
        self.client_user.refresh_from_db()
        self.assertEqual(self.client_user.profile.user_type, UserProfile.UserType.EMPLOYEE)

    def test_time_logs_only_lists_employees(self):
        self.client.login(username="admin", password="pass")
        response = self.client.get(reverse("time_logs"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "employee")
        employees = response.context["employees"]
        self.assertEqual(employees.count(), 1)

    def test_new_user_not_in_employee_dropdown(self):
        extra = User.objects.create_user(username="extra", password="pass")
        set_user_type(extra, UserProfile.UserType.CLIENT)
        self.client.login(username="admin", password="pass")
        response = self.client.get(reverse("time_logs"))
        employees = response.context["employees"]
        usernames = [e.user.username for e in employees]
        self.assertIn("employee", usernames)
        self.assertNotIn("extra", usernames)


class InvoiceAccessTests(TestCase):
    def setUp(self):
        self.admin = User.objects.create_superuser(
            username="billing_admin", password="pass", email="admin@example.com"
        )
        self.employee_user = User.objects.create_user(username="emp2", password="pass")
        set_user_type(self.employee_user, UserProfile.UserType.EMPLOYEE)
        self.client_http = Client()

    def test_non_admin_cannot_view_invoices(self):
        self.client_http.login(username="emp2", password="pass")
        response = self.client_http.get(reverse("invoice_list"))
        self.assertIn(response.status_code, (302, 403))

    def test_admin_can_view_invoices(self):
        self.client_http.login(username="billing_admin", password="pass")
        response = self.client_http.get(reverse("invoice_list"))
        self.assertEqual(response.status_code, 200)

    def test_webhook_rejects_bad_signature_when_secret_set(self):
        from django.test import override_settings

        with override_settings(STRIPE_WEBHOOK_SECRET="whsec_test"):
            response = self.client_http.post(
                reverse("stripe_webhook"),
                data=b'{"type":"invoice.paid","data":{"object":{}}}',
                content_type="application/json",
                HTTP_STRIPE_SIGNATURE="t=1,v1=bad",
            )
        self.assertEqual(response.status_code, 400)

    def test_apply_invoice_webhook_updates_status(self):
        from financial.models import BillingCustomer, Invoice
        from financial.stripe_billing import apply_stripe_invoice_event

        customer = BillingCustomer.objects.create(
            name="Acme", email="acme@example.com"
        )
        invoice = Invoice.objects.create(
            customer=customer,
            description="Work",
            amount_cents=5000,
            stripe_invoice_id="in_test_123",
            status=Invoice.Status.OPEN,
        )
        apply_stripe_invoice_event(
            {
                "id": "in_test_123",
                "status": "paid",
                "metadata": {"local_invoice_id": str(invoice.pk)},
                "hosted_invoice_url": "https://pay.stripe.com/test",
            }
        )
        invoice.refresh_from_db()
        self.assertEqual(invoice.status, Invoice.Status.PAID)
        self.assertEqual(invoice.hosted_invoice_url, "https://pay.stripe.com/test")

