File size: 5,915 Bytes
87c9973
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using Database.Models;
using Microsoft.EntityFrameworkCore;

namespace LibraryManagement.Backend.Features.Borrowings
{
    public interface IBorrowingService
    {
        Task<BorrowingDto> BorrowBookAsync(int userId, int bookId);
        Task<BorrowingDto> ReturnBookAsync(int borrowingId);
        Task<IEnumerable<BorrowingDto>> GetUserBorrowingsAsync(int userId);
        Task<IEnumerable<BorrowingDto>> GetAllBorrowingsAsync();
    }

    public class BorrowingService : IBorrowingService
    {
        private readonly LibraryManagementContext _context;
        private const decimal FinePerDay = 500;

        public BorrowingService(LibraryManagementContext context)
        {
            _context = context;
        }

        public async Task<BorrowingDto> BorrowBookAsync(int userId, int bookId)
        {
            // 1. Check for Active Membership
            var subscription = await _context.UserSubscriptions
                .Include(s => s.Membership)
                .Where(s => s.UserId == userId && s.IsActive && s.ExpiryDate > DateTime.UtcNow)
                .FirstOrDefaultAsync();

            if (subscription == null)
            {
                throw new Exception("Active membership required to borrow books.");
            }

            // 2. Check for Overdue Books or Unpaid Fines
            var hasBlockers = await _context.Borrowings
                .AnyAsync(b => b.UserId == userId && 
                    ((b.Status == "Borrowed" && b.DueDate < DateTime.UtcNow) || (b.FineAmount > 0 && !b.IsFinePaid)));

            if (hasBlockers)
            {
                throw new Exception("Borrowing blocked: You have overdue books or unpaid fines.");
            }

            // 3. Check MaxBooks limit
            var activeBorrowCount = await _context.Borrowings
                .CountAsync(b => b.UserId == userId && b.Status == "Borrowed");

            if (activeBorrowCount >= subscription.Membership.MaxBooks)
            {
                throw new Exception($"Borrowing limit reached: You can only borrow {subscription.Membership.MaxBooks} books at a time.");
            }

            // 4. Check Book Availability
            var book = await _context.Books.FindAsync(bookId);
            if (book == null || !book.IsActive || book.AvailableCopies <= 0)
            {
                throw new Exception("Book is currently unavailable.");
            }

            // 5. Create Borrowing Record
            var borrowing = new Borrowing
            {
                UserId = userId,
                BookId = bookId,
                BorrowDate = DateTime.UtcNow,
                DueDate = DateTime.UtcNow.AddDays(subscription.Membership.BorrowingDays),
                Status = "Borrowed",
                FineAmount = 0,
                IsFinePaid = false
            };

            // 6. Update Book Stock
            book.AvailableCopies--;
            if (book.AvailableCopies == 0)
            {
                book.Status = "Out Of Stock";
            }

            _context.Borrowings.Add(borrowing);
            await _context.SaveChangesAsync();

            // Load relations for mapping
            await _context.Entry(borrowing).Reference(b => b.Book).LoadAsync();
            await _context.Entry(borrowing).Reference(b => b.User).LoadAsync();

            return MapToDto(borrowing);
        }

        public async Task<BorrowingDto> ReturnBookAsync(int borrowingId)
        {
            var borrowing = await _context.Borrowings
                .Include(b => b.Book)
                .Include(b => b.User)
                .FirstOrDefaultAsync(b => b.Id == borrowingId);

            if (borrowing == null) throw new Exception("Borrowing record not found.");
            if (borrowing.Status == "Returned") throw new Exception("Book has already been returned.");

            var now = DateTime.UtcNow;
            borrowing.ReturnDate = now;
            borrowing.Status = "Returned";

            // Calculate Fine
            if (now > borrowing.DueDate)
            {
                var overdueDays = (now.Date - borrowing.DueDate.Date).Days;
                if (overdueDays > 0)
                {
                    borrowing.FineAmount = overdueDays * FinePerDay;
                }
            }

            // Update Book Stock
            var book = borrowing.Book;
            book.AvailableCopies++;
            book.Status = "Available";

            await _context.SaveChangesAsync();
            return MapToDto(borrowing);
        }

        public async Task<IEnumerable<BorrowingDto>> GetUserBorrowingsAsync(int userId)
        {
            var borrowings = await _context.Borrowings
                .Include(b => b.Book)
                .Include(b => b.User)
                .Where(b => b.UserId == userId)
                .OrderByDescending(b => b.BorrowDate)
                .ToListAsync();

            return borrowings.Select(MapToDto);
        }

        public async Task<IEnumerable<BorrowingDto>> GetAllBorrowingsAsync()
        {
            var borrowings = await _context.Borrowings
                .Include(b => b.Book)
                .Include(b => b.User)
                .OrderByDescending(b => b.BorrowDate)
                .ToListAsync();

            return borrowings.Select(MapToDto);
        }

        private static BorrowingDto MapToDto(Borrowing b)
        {
            return new BorrowingDto
            {
                Id = b.Id,
                UserId = b.UserId,
                UserEmail = b.User?.Email ?? "Unknown",
                BookId = b.BookId,
                BookTitle = b.Book?.Title ?? "Unknown",
                BorrowDate = b.BorrowDate,
                DueDate = b.DueDate,
                ReturnDate = b.ReturnDate,
                Status = b.Status,
                FineAmount = b.FineAmount,
                IsFinePaid = b.IsFinePaid
            };
        }
    }
}