"""Password reset flow tests (forgot-password / reset-password)."""
import os
import sys
import unittest
import json

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
os.environ.setdefault('DATABASE_URL', 'sqlite:///test_password_reset.db')
os.environ.setdefault('SECRET_KEY', 'test-secret-key-for-testing')
os.environ.setdefault('RATELIMIT_ENABLED', 'false')

from unittest.mock import patch

from app import create_app
from app.models import db, User


class PasswordResetTestCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.app = create_app()
        cls.app.config['TESTING'] = True
        cls.app.config['RATELIMIT_ENABLED'] = False
        cls.client = cls.app.test_client()
        db_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'instance', 'test_password_reset.db')
        if os.path.exists(db_file):
            os.unlink(db_file)
        with cls.app.app_context():
            db.create_all()
            user = User(email='reset_me@example.com', full_name='Reset Me')
            user.set_password('OriginalPass123!')
            db.session.add(user)
            db.session.commit()
            cls.user_id = user.id

    @classmethod
    def tearDownClass(cls):
        with cls.app.app_context():
            db.drop_all()
        db_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'instance', 'test_password_reset.db')
        if os.path.exists(db_file):
            os.unlink(db_file)

    def _forgot(self, email):
        with patch('app.utils.mail.send_email') as mock_send:
            resp = self.client.post(
                '/api/auth/forgot-password',
                data=json.dumps({'email': email}),
                content_type='application/json',
            )
        return resp, mock_send

    def test_forgot_password_unknown_email_generic_response(self):
        """Unknown email returns 200 with generic message (no enumeration)."""
        resp, mock_send = self._forgot('nobody@example.com')
        self.assertEqual(resp.status_code, 200)
        mock_send.assert_not_called()

    def test_forgot_password_known_email_sends_token(self):
        """Known email returns 200 and sends an email with a reset link."""
        resp, mock_send = self._forgot('reset_me@example.com')
        self.assertEqual(resp.status_code, 200)
        mock_send.assert_called_once()
        body = mock_send.call_args.kwargs.get('text_body', '')
        self.assertIn('reset-password?token=', body)

    def test_forgot_password_requires_email(self):
        resp = self.client.post(
            '/api/auth/forgot-password',
            data=json.dumps({}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 400)

    def test_reset_password_invalid_token(self):
        resp = self.client.post(
            '/api/auth/reset-password',
            data=json.dumps({'token': 'not-a-real-token', 'password': 'NewPassword123!'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 400)

    def test_reset_password_missing_token(self):
        resp = self.client.post(
            '/api/auth/reset-password',
            data=json.dumps({'password': 'NewPassword123!'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 400)

    def test_reset_password_weak_password_rejected(self):
        resp = self.client.post(
            '/api/auth/reset-password',
            data=json.dumps({'token': 'whatever', 'password': 'short'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 400)

    def test_full_reset_flow(self):
        """Token from forgot-password can be used exactly once to set a new password."""
        resp, mock_send = self._forgot('reset_me@example.com')
        self.assertEqual(resp.status_code, 200)
        text_body = mock_send.call_args.kwargs.get('text_body', '')
        token = text_body.split('reset-password?token=')[1].split()[0]

        # Use the token
        resp = self.client.post(
            '/api/auth/reset-password',
            data=json.dumps({'token': token, 'password': 'BrandNewPass456!'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 200)
        self.assertTrue(resp.get_json().get('success'))

        # Token is single-use
        resp = self.client.post(
            '/api/auth/reset-password',
            data=json.dumps({'token': token, 'password': 'AnotherPass789!'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 400)

        # New password works, old doesn't
        resp = self.client.post(
            '/api/auth/login',
            data=json.dumps({'email': 'reset_me@example.com', 'password': 'BrandNewPass456!'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 200)
        self.client.post('/api/auth/logout')
        resp = self.client.post(
            '/api/auth/login',
            data=json.dumps({'email': 'reset_me@example.com', 'password': 'OriginalPass123!'}),
            content_type='application/json',
        )
        self.assertEqual(resp.status_code, 401)


if __name__ == '__main__':
    unittest.main()
